【2021.04.26】API function calling process (Ring3 into Ring0): Part 1

Content review

kernel32.dll(ReadProcessMemory)->ntdll.dll(NtReadVirtualMemory)

mov eax, 0BA //Provide a kernel function number

mov edx, 7FFE0300 //Then provide the address of a function, which is stored at the location 7FFE0300.

This article will focus on what this function is. To understand what this function is, you also need to figure out the special meaning of the memory 7FFE0000.

_KUSER_SHARED_DATA

  • A _KUSER_SHARED_DATA structure area is defined in the user layer and kernel layer respectively, which is used for the user layer and kernel layer to share certain data.
  • They use fixed address value mapping. The _KUSER_SHARED_DATA structure area is at the user and kernel layers. The addresses are as follows:
  1. User layer address: 0x7FFE0000, which can be accessed by any 3-ring program.
  2. Kernel layer address: 0xFFDF0000

Although it points to the same physical page, it is read-only at the user layer and read-write at the kernel layer.

0x7FFE0000 (3-ring address):

0xFFDF0000 (0 ring address):

You can see that the stored content is completely consistent. Ignore other parts for now. Let’s focus on the content and review the code in the picture mov edx, 7FFE0300:

View the _KUSER_SHARED_DATA structure (0x7FFE0000) and find the location of 0x300.

The location name of 0x300 is called SystemCall. Now look back at the content review picture:

When calling an API and entering ntdll, the location (mov edx, 7FFE0300, call dword ptr [edx]) will be called, and this location stores a function address.

What we need to study now is, what function is stored in this location?

What exactly is stored in 0x7FFE0300?

Experiment: Whether to support fast calling

When the cpuid instruction is executed with eax = 1, processor characteristics information is placed in the ecx and edx registers.

Among them, edx contains a SEP bit (position index 11), which indicates whether the current processor supports the sysenter/sysexit instruction.

SEP=1: Supports sysenter/sysexit instructions.

SEP=0: The sysenter/sysexit command is not supported.

Supports sysenter/sysexit instructions: ntdll.dll!KiFastSystemCall(), not a kernel function, it is still a function in ntdll.

The sysenter/sysexit command is not supported: ntdll.dll!KiIntSystemCall().

How does the CPU know if it supports this instruction?

The cpuid instruction can be used to query a lot of CPU-related information, but when querying CPU-related information, parameters need to be passed, and the parameters are passed through the EAX register.

For example, I currently want to know whether the CPU supports the sysenter/sysexit instructions:

Write a 1 in EAX:

Then write the cpuid command:

When the cpuid instruction completes execution, it will store the results in the ecx and edx registers, so now set both ecx and edx to zero:

This way, it looks more intuitive after execution is complete.

After execution is complete:

After the execution is completed, the edx index is 11, which is the so-called SEP bit.

If the SEP bit is set to 1, it proves that the current CPU supports the sysenter/sysexit instruction.

If it is 0, it is not supported.

When the operating system starts, the operating system will first query whether the current CPU supports the sysenter/sysexit instruction through the cpuid instruction.

When supported, it writes the address of the ntdll.dll!KiFastSystemCall() function to the _KUSER_SHARED_DATA structure at offset 0x300.

If it is not supported, it will write the address of another function, that is, the function address of ntdll.dll!KiIntSystemCall() to the 0x300 offset of the _KUSER_SHARED_DATA structure.

So when you see a situation like this, you cannot arbitrarily prove what function it is. Instead, you must first go through the test. Only by passing the test can you prove which function it stores.

Which registers need to be changed to enter the 0 loop?

  1. The authority of the CS changes from 3 to 0, which means a new CS is required.
  2. The permissions of SS and CS are always the same, and a new SS is required.
  3. When permissions switch, the stack will also switch, so a new ESP is required.
  4. After entering ring 0, where the code starts to execute, EIP is required.

The difference between ntdll.dll!KiFastSystemCall() and ntdll.dll!KiIntSystemCall()

Similarity: How to find four values ​​(CS, SS, ESP, EIP), but these two functions provide different ways to find them.

The sysenter/sysexit instructions are not supported (the interrupt gate enters ring 0):

When the sysenter/sysexit instruction is not supported, the system call number (kernel function number) and parameters are pushed into the register (eax, edx), and then enter the 0 ring through the interrupt gate (int 0x2E).

For all APIs, when entering ring 0, the unified interrupt gate interrupt number is 0x2E. The position of 0x2E in the IDT table.

Supports sysenter/sysexit instructions (quick call to enter ring 0):

Put the value at the top of the current stack into edx, and edx is used to find where the parameters are.

eax still stores the system call number (kernel function number).

Then the interrupt gate is not used, but sysenter is used.

Why is it called quick call?

When the interrupt gate enters ring 0, the required CS and EIP are in the IDT table and the memory needs to be checked (SS and ESP are provided by TSS).

If the CPU supports the sysenter instruction, the operating system will store the values ​​of CS, SS, ESP, and EIP in advance in theMSR register .

When the sysenter instruction is executed, the CPU will directly write the value in the MSR register to the relevant register. There is no process of reading the memory, so it is called quick call, the essence is the same.

Guess you like

Origin blog.csdn.net/qq_18120361/article/details/116166464