This is the third part of the WRK series. For your convenience you can find other parts in the table of contents in Part 1 — Compiling and debugging
Today we are going to write a very simple hello world in the kernel space. Let’s go.
Kernel
First, we need to add new syscall to the table of services. Go to the file base\ntos\ke\i386\systable.asm
. In line 392 add the following:
1 |
TABLE_ENTRY HelloKernel, 1, 1 |
This specifies that at least one argument must be passed (first 1) and at most one as well (second 1).
Next, replace TABLE_END 295
with TABLE_END 295
.
Now we need to add a stub. Go to base\ntos\ke\i386\sysstubs.asm
and add the following in line 2485:
1 2 3 4 5 6 7 8 |
SYSSTUBS_ENTRY1 296, HelloKernel, 1 SYSSTUBS_ENTRY2 296, HelloKernel, 1 SYSSTUBS_ENTRY3 296, HelloKernel, 1 SYSSTUBS_ENTRY4 296, HelloKernel, 1 SYSSTUBS_ENTRY5 296, HelloKernel, 1 SYSSTUBS_ENTRY6 296, HelloKernel, 1 SYSSTUBS_ENTRY7 296, HelloKernel, 1 SYSSTUBS_ENTRY8 296, HelloKernel, 1 |
Now the linker expects a method with one int parameter. We need to implement it, so let’s go to base\ntos\ps\psquery.c
and add the following in line 4220:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
NTSTATUS NtHelloKernel( int count ) { int i; PAGED_CODE(); for (i=0; i < count; ++i) { DbgPrint("Hello world: [%d]",i); } return STATUS_SUCCESS; } |
Recompile the kernel and reboot the os.
User mode
Now we need to execute the method from the user space. In theory we should implement a wrapper for the syscall in a DLL. Next, we would just write an application calling the method via the DLL. But, we are going to do it a little differently:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void CallNtHelloKernel( int count ) { void** stackFrame = (void*)(&count); __asm { mov eax, 0x0128 mov edx, stackFrame int 0x2E } } int main(int argc, char* argv[]) { printf("calling HelloKernel\n"); // use new system service call CallNtHelloKernel(5); } |
We first define a method with exactly the same signature as the method in the kernel. Next, we define a stack frame for the parameter. Finally, we switch to the kernel space with the interrupt. eax
register holds the ordinal of the syscall (remember what number we put in sysstubs.asm
?) and edx
holds a stack frame.
Finally, we just call this method from the C code.
Compile the code to a binary. This binary is completely unrelated to the WRK, it is just an application like every other. Copy it to your VM and run in your modified kernel. As a bonus, try to run it with the unmodified WRK kernel and see what happens.