/* driver loader is used to install the driver via service manager it also sends some commands to driver usage is simple: load driver_name driver_display_name path you can use loadhook.bat second thread was added to receive events from driver it uses asynchronous IO to manage this see description in the main driver file for more */ #include #include #include #include "../drvcomm.h" int work=0; //indicator of application status HANDLE notify_event; //sync event between notify thread and main thread int notify_type; //type of driver notification DWORD notify_data; //driver notification data HANDLE notify_dev; //notify thread device file /* second thread manages notifications from driver we do not take any action if something in this thread failed however, for the functionality of our program this thread is important so in real world program this should be handled too also synchronization is missing in this example */ DWORD WINAPI notify_thread(LPVOID args) { printf("Notify thread runs\n"); HANDLE notify_dev=CreateFile(args,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL); if (notify_dev==INVALID_HANDLE_VALUE) notify_dev=0; if (notify_dev) { OVERLAPPED overlapped; overlapped.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if (overlapped.hEvent) { while (work) { DRVCOMM_REQUEST_BUFFER buf_req; DRVCOMM_RESPONSE_BUFFER buf_res; memset(&buf_req,0,sizeof(buf_req)); memset(&buf_res,0,sizeof(buf_res)); DWORD bytes; int ret=DeviceIoControl(notify_dev,IOCTL_NOTIFY,NULL,0,&buf_res,sizeof(buf_res),&bytes,&overlapped); int err=ret?0:GetLastError(); if (err==ERROR_IO_PENDING) { if (GetOverlappedResult(notify_dev,&overlapped,&bytes,TRUE)) err=0; else err=GetLastError(); } if (err) { if (err==ERROR_OPERATION_ABORTED) printf("Notify thread received exit signal\n"); else printf("Notify thread error: unexpected DeviceIoControl error %d\n",err); break; } if (!bytes || !buf_res.status) { printf("Notify thread communication error: bytes=%d; buf_res.status=0x%.8X\n",bytes,buf_res.status); break; } /* we've received notification from driver we'll contact second thread for user input */ HKEY key; char name[512],*svc_name,imagepath[512],svc_path[512]; DWORD imagepath_len=sizeof(imagepath); memset(imagepath,0,imagepath_len); err=0; switch (buf_res.type) { case RESPONSE_NOTIFY_PROCESS_CHECK: printf("Notify thread: RESPONSE_NOTIFY_PROCESS_CHECK received, you should make a decision:\n"); notify_type=RESPONSE_NOTIFY_PROCESS_CHECK; notify_data=buf_res.parameters.process_check.pid; printf("permit 0x%.8X access for process pid=%d to process pid=%d?\n", buf_res.parameters.process_check.access,buf_res.parameters.process_check.caller_pid,notify_data); PulseEvent(notify_event); WaitForSingleObject(notify_event,INFINITE); printf("Notify thread wait done, answer = %s\n",notify_data?"deny":"permit"); buf_req.parameters.process_check.permit=notify_data; ret=DeviceIoControl(notify_dev,IOCTL_NOTIFY_PROCESS_CHECK,&buf_req,sizeof(buf_req),NULL,0,&bytes,&overlapped); if (!ret && (GetLastError()==ERROR_IO_PENDING)) GetOverlappedResult(notify_dev,&overlapped,&bytes,TRUE); break; case RESPONSE_NOTIFY_LOAD_DRIVER_CHECK: /* we've got a system registry path to Services in HKLM, we open it and read ImagePath from there */ notify_type=RESPONSE_NOTIFY_LOAD_DRIVER_CHECK; notify_data=(ULONG)&buf_res.parameters.load_driver_check.regpath; err=3; sprintf(name,"%S\0",&buf_res.parameters.load_driver_check.regpath); svc_name=strrchr(name,'\\'); svc_name++; if (svc_name && *svc_name) { err--; sprintf(svc_path,"SYSTEM\\CurrentControlSet\\Services\\%s\0",svc_name); if (ret=RegOpenKeyEx(HKEY_LOCAL_MACHINE,svc_path,0,KEY_QUERY_VALUE,&key)==ERROR_SUCCESS) { err--; printf("Notify thread: RESPONSE_NOTIFY_LOAD_DRIVER_CHECK received, you should make a decision:\n"); if (ret=RegQueryValueEx(key,"ImagePath",NULL,NULL,imagepath,&imagepath_len)==ERROR_SUCCESS) { err--; printf("permit loading driver ServiceName=%s ImagePath=%s for process pid=%d?\n", svc_name,imagepath,buf_res.parameters.load_driver_check.caller_pid); PulseEvent(notify_event); WaitForSingleObject(notify_event,INFINITE); printf("Notify thread wait done, answer = %s\n",notify_data?"deny":"permit"); } } } if (err) { printf("Notify thread: RESPONSE_NOTIFY_LOAD_DRIVER_CHECK received, but error occurred: "); switch (err) { case 1:printf("RegQueryValueEx for ImagePath failed with code %d",ret); break; case 2:printf("RegOpenKeyEx failed with code %d",ret); break; case 3:printf("invalid service name"); break; } //something went wrong -> deny access printf(" -> denying access"); notify_data=0; } buf_req.parameters.load_driver_check.permit=notify_data; ret=DeviceIoControl(notify_dev,IOCTL_NOTIFY_LOAD_DRIVER_CHECK,&buf_req,sizeof(buf_req),NULL,0,&bytes,&overlapped); if (!ret && (GetLastError()==ERROR_IO_PENDING)) GetOverlappedResult(notify_dev,&overlapped,&bytes,TRUE); break; default: printf("Notify thread error: unknown response buffer type\n"); } } CloseHandle(overlapped.hEvent); } else printf("Notify thread error: unable to create event\n"); CloseHandle(notify_dev); } else printf("Notify thread error: unable to connect device\n"); printf("Notify thread terminates\n"); ExitThread(0); } int main(int argc,char **argv) { /* this version uses ZwQueryInformationThread in kernel driver to determine process that owns specific thread, unfortunately ntoskrnl.exe exports ZwQueryInformationThread in XP and higher, but the implementation is also available in W2k, so we will do a little hack here and from user mode ntdll.dll we retrieve an index to SDT and send it to kernel driver to call ZwQueryInformationThread using this index we could hardcode the index for W2k but this way it is more elegant as we don't need to care about service pack differences this is quite simple solution but it works only if this function is not hooked in ntdll.dll, if the function is hooked (and index is rewritten) this hack fails, however, in this case we should load and map ntdll.dll from disk to get raw unhooked image and find our function address in export table and retrieve the index from the image then, this is not implemented in this version, we assume clear (not hooked) environment */ ULONG SDT_index_ZwQIT=0; HMODULE ntdll=GetModuleHandle("ntdll.dll"); PVOID ZwQIT=GetProcAddress(ntdll,"ZwQueryInformationThread"); if (ZwQIT) SDT_index_ZwQIT=*((PULONG)((UCHAR *)ZwQIT+1)); printf("SDT_index_ZwQIT = 0x%.8X\n",SDT_index_ZwQIT); if (SDT_index_ZwQIT>0x0200) SDT_index_ZwQIT=0; if (!SDT_index_ZwQIT) { printf("error: unable to retrieve valid SDT index for ZwQueryInformationThread\n"); return 0; } printf("Opening service manager ...\n"); HANDLE scm=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE); if(scm) { printf("Creating service ...\n"); HANDLE svc=CreateService(scm,argv[1],argv[2],SERVICE_START | DELETE | SERVICE_STOP,SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START,SERVICE_ERROR_IGNORE,argv[3],NULL,NULL,NULL,NULL,NULL); if(!svc) { printf("Creating failed, trying to open service ...\n"); svc=OpenService(scm,argv[1],SERVICE_START | DELETE | SERVICE_STOP); } if(svc) { printf("Starting service\n"); StartService(svc,0,NULL); printf("Creating communication device ...\n"); /* for MSTS we need to use Global namespace to access our device */ char dev_name[256]; if ((GetVersion()&0xFF)>=5) sprintf(dev_name,"\\\\.\\Global\\%s\0",argv[1]); else sprintf(dev_name,"\\\\.\\%s\0",argv[1]); HANDLE dev=CreateFile(dev_name,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL); if (dev==INVALID_HANDLE_VALUE) dev=0; if (dev) { printf("Starting notify thread\n"); work=1; DWORD tid; HANDLE thread=CreateThread(NULL,0,notify_thread,dev_name,0,&tid); if (thread==INVALID_HANDLE_VALUE) thread=0; notify_event=CreateEvent(NULL,TRUE,FALSE,NULL); if (thread && notify_event) { CloseHandle(thread); printf("Press Enter to send IOCTL_HOOK_START\n"); getchar(); DRVCOMM_REQUEST_BUFFER buf_req; DRVCOMM_RESPONSE_BUFFER buf_res; DWORD bytes=0,ret; buf_req.parameters.init.SDT_index_ZwQueryInformationThread=SDT_index_ZwQIT; /* send command to our driver, no input buffer is used here output buffer contains status value only */ memset(&buf_res,0,sizeof(buf_res)); ret=DeviceIoControl(dev,IOCTL_HOOK_START,&buf_req,sizeof(buf_req),&buf_res,sizeof(buf_res),&bytes,NULL); printf("DeviceIoControl returned code 0x%.8X, status 0x%.8X and %d bytes\n",ret,buf_res.status,bytes); if (buf_res.status) { /* this is a loop for driver rules managment */ printf("\nenter commands to protect/disable protection of processes\n" "to enable protection for process write 'e PID'\n" "to disable protection for process write 'd PID'\n" "to answer notify check write 'p 0/1' - '0' for deny and '1' for permit\n" "current process id = %d\n" "write 'x 0' for end\n",GetCurrentProcessId()); while (work) { bytes=0; char ch_type; int obj_id=0; scanf("%c %d",&ch_type,&obj_id); getchar(); int enable; ULONG code; switch (ch_type) { case 'd': case 'e': enable=ch_type=='e'; code=IOCTL_PROTECT_PROCESS; buf_req.parameters.protect_process.pid=obj_id; buf_req.parameters.protect_process.enable=enable; ret=DeviceIoControl(dev,code,&buf_req,sizeof(buf_req),&buf_res,sizeof(buf_res),&bytes,NULL); printf("DeviceIoControl returned code 0x%.8X, status 0x%.8X and %d bytes\n",ret,buf_res.status,bytes); break; case 'p': notify_data=!obj_id; //we set protect TRUE or FALSE -> protect = !permit PulseEvent(notify_event); break; case 'x': work=0; break; default: printf("unknown command type %c\n",ch_type); } } bytes=0; printf("Press Enter to send IOCTL_HOOK_STOP\n"); getchar(); ret=DeviceIoControl(dev,IOCTL_HOOK_STOP,NULL,0,&buf_res,sizeof(buf_res),&bytes,NULL); printf("DeviceIoControl returned code 0x%.8X, status 0x%.8X and %d bytes\n",ret,buf_res.status,bytes); //cancel possible waiting PulseEvent(notify_event); } printf("Press Enter to send close device handle\n"); getchar(); CloseHandle(dev); } else printf("error: unable to create notify thread\n"); if (notify_event) CloseHandle(notify_event); } else printf("error: unable to create communication device\n"); printf("Press Enter to stop and delete service \n"); getchar(); SERVICE_STATUS status; ControlService(svc,SERVICE_CONTROL_STOP,&status); DeleteService(svc); CloseServiceHandle(svc); } else printf("error: unable to create/open service\n"); CloseServiceHandle(scm); } else printf("error: unable to open manager\n"); return 0; }