Before getting started with RING 0, I thought I had to learn a lot and had a lot of knowledge to drive development. However, I thought about it later. Other programs that do not directly access the hardware, such as the driver shell, cannot directly access the hardware for compatibility, that is, those that are based on the hardware abstraction layer, in addition, most of the APIS provided by the system are used (the APIs used in RING0 are called NATIVE APIs ). Everything becomes simple at once, unless you want to write optimization hardware or overclock programs by using reverse hardware vendor drivers.
Although this is purely static analysis, I hope that by analyzing the entire driver, you will understand some mechanisms under RING0 and understand how to locate code in dynamic debugging.
Before getting started, thanks to rockhard's source code and compiled drivers, so that I don't have to learn how to use WINDDK. You can get it in the attachment of the link below:
Http://bbs.pediy.com/showthread.php? S = & threadid = 35626.
Preliminary Implementation of system-level interception applications to obtain the hard disk physical serial number
Rockhard's goal in the above article is to simply modify the source code of the REGMON driver to block the physical serial number of the application from the hard disk. It is inevitable that there are deficiencies. Personal immature comments on Source Code are not targeted at Rockhard.
When learning the reverse direction, my approach is to first look at how the advanced language code is compiled. Maybe I still have to learn how to use WINDDK, write code, compile and disassemble it, and it will answer some questions. Next let me explain some of the mechanisms in RING0 by comparing the source code with the disassembly code.
NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
DriverEntry, the entry function of the driver, and some initialization operations of the driver will be performed here. Like RING3, the program accesses DriverObject and RegistryPath through the stack. In IDA, the name of the second parameter is a little different after disassembly: "; int _ stdcall start (PDRIVER_OBJECT DriverObject, HANDLE Handle)", but it doesn't matter. We know, in fact, it is the same.
. Text: 000105A0 push 7
. Text: 000105A2 pop ecx
. Text: 000105A3 mov esi, offset s_DeviceHdhook; "\ Device \ HDHOOK"
. Text: 000105A8 lea edi, [ebp + regnameNt]
. Text: 000105AE push 9
. Text: 000105B0 rep movsd
. Text: 000105B2 movsw
. Text: 000105B4 pop ecx
. Text: 000105B5 mov esi, offset s_DosdevicesHdh; "\ DosDevices \ HDHOOK"
. Text: 000105BA lea edi, [ebp + regnameDos]
. Text: 000105C0 push 206B6444h; Tag
. Text: 000105C5 rep movsd
. Text: 000105C7 movsw
. Text: 000105C9 mov esi, offset s_Start; "Start"
. Text: 000105CE lea edi, [ebp + SourceString]
. Text: 000105D1 movsd
. Text: 000105D2 movsd
. Text: 000105D3 movsd
. Text: 000105D4 mov esi, [ebp + Handle]
. Text: 000105D7 movzx eax, word ptr [esi]
. Text: 000105DA inc eax
. Text: 000105DB inc eax
. Text: 000105DC push eax; NumberOfBytes
. Text: 000105DD push 1; PoolType
. Text: 000105DF call ds: ExAllocatePoolWithTag
The above Code is based on the definitions of the following three local variables. The compiler will generate a piece of code, first move these characters into the stack, and then use them.
WCHAR deviceNameBuffer [] = L "\ Device \" DRIVER_NAME;
WCHAR deviceLinkBuffer [] = L "\ DosDevices \" DRIVER_NAME;
WCHAR startValueBuffer [] = L "Start ";
From a reverse perspective, if a static character variable is changed to a global variable, you can get a higher running efficiency and a smaller program. It is worth noting that the call of native api, the first line of code after the parameter is written into the stack, is still the initialization of local variables, which reminds me of the introduction of distortion and deformation. The Code of 000105D4 is the second parameter to get DriverEntry from the stack. It is a UNICODE_STRING structure and the description found in MSDN (unless otherwise specified, all materials are retrieved from MSDN ):
Typedef struct _ UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING * PUNICODE_STRING;
From the code at 000105D7, the structure is:
WORD Length
WORD MaximumLength
DWORD Buffer (pointer to character)
Through Reverse RtlInitUnicodeString, we can see that the MaximumLength member in the above structure is actually equivalent to Length + sizeof (UNICODE_NULL ). About calling the third parameter PoolType of ExAllocatePoolWithTag:
Typedef enum _ POOL_TYPE {
NonPagedPool,
PagedPool,
NonPagedPoolMustSucceed,
DontUseThisType,
NonPagedPoolCacheAligned,
PagedPoolCacheAligned,
NonPagedPoolCacheAlignedMustS
} POOL_TYPE;
It seems to have nothing to do with PUSH 1. Now let's look at the source code:
RegistryPath. Buffer = ExAllocatePool (PagedPool,
RegistryPath-> Length + sizeof (UNICODE_NULL ));
The parameter used here is PagedPool. For the enum type definition, each member represents a number that increases from 0. The PagedPool is in the second position, so it is 1. ExAllocatePool used by source code,