From 2acec63b2ed75bf4b71ad257db573c4b8f9639e7 Mon Sep 17 00:00:00 2001 From: tumagonx Date: Tue, 8 Aug 2017 10:54:53 +0700 Subject: initial commit --- MAKEFILE | 8 + Sources | 9 + TODO | 122 +++ accessmask.c | 693 ++++++++++++ accessmask.h | 55 + atom.c | 187 ++++ atom.h | 78 ++ boprot.c | 173 +++ boprot.h | 33 + debug.c | 162 +++ debug.h | 47 + dirobj.c | 153 +++ dirobj.h | 78 ++ driver.c | 1237 ++++++++++++++++++++++ driver.h | 90 ++ driver.sln | 21 + driver.vcproj | 341 ++++++ driverobj.c | 205 ++++ driverobj.h | 68 ++ event.c | 259 +++++ event.h | 105 ++ file.c | 665 ++++++++++++ file.h | 273 +++++ hookproc.c | 1799 ++++++++++++++++++++++++++++++++ hookproc.h | 460 ++++++++ i386.c | 178 ++++ i386.h | 184 ++++ job.c | 157 +++ job.h | 68 ++ learn.c | 1414 +++++++++++++++++++++++++ learn.h | 43 + log.c | 459 ++++++++ log.h | 236 +++++ media.c | 444 ++++++++ media.h | 40 + misc.c | 943 +++++++++++++++++ misc.h | 101 ++ mutant.c | 151 +++ mutant.h | 78 ++ network.c | 715 +++++++++++++ network.h | 57 + ntproto.h | 289 +++++ pathproc.c | 1093 +++++++++++++++++++ pathproc.h | 67 ++ policy.c | 3243 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ policy.h | 344 ++++++ port.c | 318 ++++++ port.h | 162 +++ process.c | 1270 ++++++++++++++++++++++ process.h | 215 ++++ procname.c | 677 ++++++++++++ procname.h | 68 ++ registry.c | 402 +++++++ registry.h | 140 +++ resource.h | 15 + section.c | 285 +++++ section.h | 112 ++ semaphore.c | 161 +++ semaphore.h | 69 ++ symlink.c | 185 ++++ symlink.h | 78 ++ sysinfo.c | 197 ++++ sysinfo.h | 190 ++++ time.c | 190 ++++ time.h | 68 ++ timer.c | 151 +++ timer.h | 78 ++ token.c | 250 +++++ token.h | 145 +++ userland.c | 573 ++++++++++ userland.h | 133 +++ vdm.c | 227 ++++ vdm.h | 72 ++ wireless.c | 338 ++++++ wireless.h | 40 + 75 files changed, 24434 insertions(+) create mode 100644 MAKEFILE create mode 100644 Sources create mode 100644 TODO create mode 100644 accessmask.c create mode 100644 accessmask.h create mode 100644 atom.c create mode 100644 atom.h create mode 100644 boprot.c create mode 100644 boprot.h create mode 100644 debug.c create mode 100644 debug.h create mode 100644 dirobj.c create mode 100644 dirobj.h create mode 100644 driver.c create mode 100644 driver.h create mode 100644 driver.sln create mode 100644 driver.vcproj create mode 100644 driverobj.c create mode 100644 driverobj.h create mode 100644 event.c create mode 100644 event.h create mode 100644 file.c create mode 100644 file.h create mode 100644 hookproc.c create mode 100644 hookproc.h create mode 100644 i386.c create mode 100644 i386.h create mode 100644 job.c create mode 100644 job.h create mode 100644 learn.c create mode 100644 learn.h create mode 100644 log.c create mode 100644 log.h create mode 100644 media.c create mode 100644 media.h create mode 100644 misc.c create mode 100644 misc.h create mode 100644 mutant.c create mode 100644 mutant.h create mode 100644 network.c create mode 100644 network.h create mode 100644 ntproto.h create mode 100644 pathproc.c create mode 100644 pathproc.h create mode 100644 policy.c create mode 100644 policy.h create mode 100644 port.c create mode 100644 port.h create mode 100644 process.c create mode 100644 process.h create mode 100644 procname.c create mode 100644 procname.h create mode 100644 registry.c create mode 100644 registry.h create mode 100644 resource.h create mode 100644 section.c create mode 100644 section.h create mode 100644 semaphore.c create mode 100644 semaphore.h create mode 100644 symlink.c create mode 100644 symlink.h create mode 100644 sysinfo.c create mode 100644 sysinfo.h create mode 100644 time.c create mode 100644 time.h create mode 100644 timer.c create mode 100644 timer.h create mode 100644 token.c create mode 100644 token.h create mode 100644 userland.c create mode 100644 userland.h create mode 100644 vdm.c create mode 100644 vdm.h create mode 100644 wireless.c create mode 100644 wireless.h diff --git a/MAKEFILE b/MAKEFILE new file mode 100644 index 0000000..d5bedee --- /dev/null +++ b/MAKEFILE @@ -0,0 +1,8 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def + diff --git a/Sources b/Sources new file mode 100644 index 0000000..a6c0fad --- /dev/null +++ b/Sources @@ -0,0 +1,9 @@ +TARGETNAME=ozone +TARGETTYPE=DRIVER + +TARGETPATH=. +INCLUDES= $(BASEDIR)\inc;. + +TARGETLIBS=$(DDK_LIB_PATH)\tdi.lib + +SOURCES=driver.c file.c registry.c policy.c process.c pathproc.c hookproc.c learn.c misc.c section.c sysinfo.c event.c semaphore.c time.c network.c accessmask.c log.c job.c mutant.c port.c symlink.c timer.c token.c driverobj.c atom.c vdm.c i386.c procname.c userland.c debug.c media.c boprot.c dirobj.c \ No newline at end of file diff --git a/TODO b/TODO new file mode 100644 index 0000000..c618004 --- /dev/null +++ b/TODO @@ -0,0 +1,122 @@ +TODO: + +append only files can be achieved by making sure that offsets passed to writefile are not less than the total size of the file + +disable all non-TCP/IP / netbios protocols by default (with an additional option to enable) +(connect to \Device\AFD disable non tcp/ip stuff) (\device\netbios) + +svchost.exe needs to be jailed by DLLs... each DLL will have its own policy + +policy_include: additional.policy + +add ability to deny logons to certain users + +add a "signature" rule.. LocalSystem execution of different processes (especially cmd.exe) should be logged and possibly denied? + +allow occasional rules to go through w/o logging? especially file & registry? + +investigate SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\IEXPLORE.EXE + + +disable execution of certain applications based on their version (i.e. vulnerable IE) (from okena) +add sniffer & non-ip protocol detection (from okena) +COM/ActiveX interception +support for application clauses (chapter 6) + +when creating an "exception" rule, MC can/should ask for whether the exception rule should be created on +the selected agent only, all agents of a certain type or all agents of all types + + +block debuggers + +non-GUI/system apps cannot use GUI calls? + + +in server paranoid mode, allow only c:\program files\*.exe and c:\windows\*.exe to execute? +trusted path execution (execution of binaries from non-trusted directories (i.e world-writable)) +(A trusted path is one that is inside is a root owned directory that is not group or world writable.) + +explorer option.. "Run process in a sandbox.." brings up a gui that asks whether to allow file, reg, network access? + +port to Itanium/AMD64 + +see if we can take over the job of a buffer overflow security exception handler +on win2k3 install custom BO exception handler that terminates a process + +need to be able to control access to all device drivers (is this already handled by intercepting createfile?) is there another way to obtain a handle to a kernel driver? +disable modem access, etc + +raw devices of all (mounted?) filesystems should be read-only + +copy in all unicode strings, check them and then pass the kernel copies to the kernel to avoid race conditions? + +disable our driver if loading using LastKnownGood configuration (notify MC?) + +restrict reboot capability and certain programs only to interactive sessions?! + +add ability to load what programs are allowed to run? (sha1 hashes, signed binaries) + +investigate kernel32!CreateHardLink + +dll_all: log will also log all section rules since RULE_DLL will be converted to RULE_SECTION + +protect crypto keys + +use ZwQueryProcessInfo ProcessVmCounters to keep track of amount of allocated process memory (execution time can be limited using job objects?! memory limit too?) +(or simply hijack malloc & free) + +device naming on terminal servers + +have a webpage which lists new vulnerabilities and whether our system would automatically protect against it + +deallocate allocated virtual memory that was used by AS randomization once the process is loaded and initialized (what about dynamically loaded DLLs)? + +create a policy check tool.. one of the things to lookout for is using "eq" and then specifying regex chars like * or ? in the filename + +interactive learning mode + +policy_ask user app should not run as an interative service but rather as a separate app running as a particular user + +IIS install should scan the registry for any known virtual roots and automatically add them to the policy.. same for other apps + +make sure that file-system protection cannot be subverted by accessing files by other means (\\127.0.0.1\share\file) + +per-group policy, per-user global policy + + +network connect should be able to specify ports and not just ip addresses +address eq "127.0.0.1:443" then permit +address eq "0:443" then deny +address eq "\\UNCpath\blah" then log +address eq "www.porn.com:80" then deny + + +new product idea: Solaris BSM-like auditing (http://www.securityfocus.com/infocus/1362) for Windows +(compare to what audit logs native Windows Group/Security Policies can already generate) +posix 1003e + + +layers: + +desktop +web server (iis, apache, netscape) +database server (oracle, MS SQL / access, Sybase, DB2, Informix, Interbase, MySQL) +terminal server +mail server +VPN server / remote access server + +dns server +dhcp server +wins server +streaming media server +domain controller +file and print server +(application server – websphere, BEA websphere) +(collaboration server – IBM Lotus Domino) + + +client policies: + email (outlook, outlook express, eudora, netscape) + browsers (IE, netscape, opera) + IM (aol, yahoo!, msn, icq) + others (ms office, napster) diff --git a/accessmask.c b/accessmask.c new file mode 100644 index 0000000..e91129f --- /dev/null +++ b/accessmask.c @@ -0,0 +1,693 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * accessmask.c + * + * Abstract: + * + * This module implements various ACCESS_MASK decoding routines. + * + * Author: + * + * Eugene Tsyrklevich 18-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include "accessmask.h" + + + +/* + * Get_FILE_OperationType() + * + * Description: + * This function decodes file operation types such as GENERIC_READ and DELETE and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_FILE_OperationType(ACCESS_MASK DesiredAccess) +{ + UCHAR OperationType = 0; +// int FileAll = MAXIMUM_ALLOWED | GENERIC_ALL | STANDARD_RIGHTS_REQUIRED | +// STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY; //FILE_ALL_ACCESS + + + if ( IS_BIT_SET(DesiredAccess, GENERIC_READ) || + IS_BIT_SET(DesiredAccess, GENERIC_ALL) || + IS_BIT_SET(DesiredAccess, FILE_READ_DATA) || +// IS_BIT_SET(DesiredAccess, FILE_READ_ACCESS) || +// IS_BIT_SET(DesiredAccess, FILE_LIST_DIRECTORY) || + IS_BIT_SET(DesiredAccess, FILE_READ_ATTRIBUTES) || + IS_BIT_SET(DesiredAccess, FILE_READ_EA) || + IS_BIT_SET(DesiredAccess, SYNCHRONIZE) || + IS_BIT_SET(DesiredAccess, STANDARD_RIGHTS_READ) || + IS_BIT_SET(DesiredAccess, FILE_ALL_ACCESS) || + DesiredAccess == 0) + + OperationType |= OP_READ; + + + if ( IS_BIT_SET(DesiredAccess, GENERIC_WRITE) || + IS_BIT_SET(DesiredAccess, GENERIC_ALL) || + IS_BIT_SET(DesiredAccess, FILE_WRITE_DATA) || +// IS_BIT_SET(DesiredAccess, FILE_WRITE_ACCESS) || +// IS_BIT_SET(DesiredAccess, FILE_ADD_FILE) || + IS_BIT_SET(DesiredAccess, FILE_WRITE_ATTRIBUTES) || + IS_BIT_SET(DesiredAccess, FILE_WRITE_EA) || + IS_BIT_SET(DesiredAccess, FILE_APPEND_DATA) || +// IS_BIT_SET(DesiredAccess, FILE_ADD_SUBDIRECTORY) || +// IS_BIT_SET(DesiredAccess, FILE_CREATE_PIPE_INSTANCE) || + IS_BIT_SET(DesiredAccess, FILE_DELETE_CHILD) || + IS_BIT_SET(DesiredAccess, DELETE) || //XXX it's own category now? + IS_BIT_SET(DesiredAccess, WRITE_DAC) || + IS_BIT_SET(DesiredAccess, WRITE_OWNER) || + IS_BIT_SET(DesiredAccess, FILE_ALL_ACCESS) ) + + OperationType |= OP_WRITE; + + + if ( IS_BIT_SET(DesiredAccess, GENERIC_EXECUTE) || + IS_BIT_SET(DesiredAccess, GENERIC_ALL) || + IS_BIT_SET(DesiredAccess, FILE_EXECUTE) || +// IS_BIT_SET(DesiredAccess, FILE_TRAVERSE) || + IS_BIT_SET(DesiredAccess, FILE_ALL_ACCESS) ) + + OperationType |= OP_EXECUTE; + + if (OperationType == 0) +// OperationType = OP_READ | OP_WRITE | OP_EXECUTE; + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("Get_FILE_OperationType: Unknown desired access mask %x\n", DesiredAccess)); + + + return OperationType; +} + + + +/* + * DecodeFileOperationType() + * + * Description: + * This function decodes file operation types such as GENERIC_READ and DELETE and prints them out (for debugging) + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * Nothing. + */ + +void +DecodeFileOperationType(ACCESS_MASK DesiredAccess) +{ + UCHAR OperationType = 0; + int FileAll = MAXIMUM_ALLOWED | GENERIC_ALL | STANDARD_RIGHTS_REQUIRED | + STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY; + + + if ( (DesiredAccess & GENERIC_READ) == GENERIC_READ) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("GENERIC_READ %x\n", GENERIC_READ)); + + if ( (DesiredAccess & GENERIC_WRITE) == GENERIC_WRITE) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("GENERIC_WRITE %x\n", GENERIC_WRITE)); + + if ( (DesiredAccess & GENERIC_EXECUTE) == GENERIC_EXECUTE) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("GENERIC_EXECUTE %x\n", GENERIC_EXECUTE)); + + if ( (DesiredAccess & STANDARD_RIGHTS_READ) == STANDARD_RIGHTS_READ) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("STANDARD_RIGHTS_READ %x\n", STANDARD_RIGHTS_READ)); + + if ( (DesiredAccess & SYNCHRONIZE) == SYNCHRONIZE) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("SYNCHRONIZE %x\n", SYNCHRONIZE)); + + if ( (DesiredAccess & MAXIMUM_ALLOWED) == MAXIMUM_ALLOWED) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("MAXIMUM_ALLOWED %x\n", MAXIMUM_ALLOWED)); + + if ( (DesiredAccess & GENERIC_ALL) == GENERIC_ALL) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("GENERIC_ALL %x\n", GENERIC_ALL)); + + if ( (DesiredAccess & STANDARD_RIGHTS_REQUIRED) == STANDARD_RIGHTS_REQUIRED) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("STANDARD_RIGHTS_REQUIRED %x\n", STANDARD_RIGHTS_REQUIRED)); + + if ( (DesiredAccess & STANDARD_RIGHTS_ALL) == STANDARD_RIGHTS_ALL) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("STANDARD_RIGHTS_ALL %x\n", STANDARD_RIGHTS_ALL)); + + if ( (DesiredAccess & SPECIFIC_RIGHTS_ALL) == SPECIFIC_RIGHTS_ALL) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("SPECIFIC_RIGHTS_ALL %x\n", SPECIFIC_RIGHTS_ALL)); + + if ( (DesiredAccess & ACCESS_SYSTEM_SECURITY) == ACCESS_SYSTEM_SECURITY) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("ACCESS_SYSTEM_SECURITY %x\n", ACCESS_SYSTEM_SECURITY)); + + if ( (DesiredAccess & WRITE_OWNER) == WRITE_OWNER) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("WRITE_OWNER %x\n", WRITE_OWNER)); + + if ( (DesiredAccess & WRITE_DAC) == WRITE_DAC) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("WRITE_DAC %x\n", WRITE_DAC)); + + if ( (DesiredAccess & DELETE) == DELETE) + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("DELETE %x\n", DELETE)); + + + if ( IS_BIT_SET(DesiredAccess, GENERIC_READ) || + IS_BIT_SET(DesiredAccess, (STANDARD_RIGHTS_READ | SYNCHRONIZE )) || + IS_BIT_SET(DesiredAccess, FileAll) ) + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("OP_READ\n")); + + + if ( IS_BIT_SET(DesiredAccess, GENERIC_WRITE) || + IS_BIT_SET(DesiredAccess, (DELETE | WRITE_DAC | WRITE_OWNER)) || + IS_BIT_SET(DesiredAccess, FileAll) ) + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("OP_WRITE\n")); + + + if ( IS_BIT_SET(DesiredAccess, GENERIC_EXECUTE) || + IS_BIT_SET(DesiredAccess, FileAll) ) + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("OP_EXECUTE\n")); +} + + + +/* + * Get_NAMEDPIPE_OperationType() + * + * Description: + * This function decodes named pipe operation types such as GENERIC_READ and DELETE and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_NAMEDPIPE_OperationType(ACCESS_MASK DesiredAccess) +{ + return Get_FILE_OperationType(DesiredAccess); +} + + + +/* + * Get_MAILSLOT_OperationType() + * + * Description: + * This function decodes mailslot operation types such as GENERIC_READ and DELETE and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_MAILSLOT_OperationType(ACCESS_MASK DesiredAccess) +{ + return Get_FILE_OperationType(DesiredAccess); +} + + + +/* + * Get_REGISTRY_OperationType() + * + * Description: + * This function decodes registry operation types such as KEY_QUERY_VALUE and DELETE and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_REGISTRY_OperationType(ACCESS_MASK DesiredAccess) +{ + UCHAR OperationType = 0; + + + if ( IS_BIT_SET(DesiredAccess, GENERIC_READ) || + IS_BIT_SET(DesiredAccess, KEY_QUERY_VALUE) || + IS_BIT_SET(DesiredAccess, KEY_ENUMERATE_SUB_KEYS) || + IS_BIT_SET(DesiredAccess, MAXIMUM_ALLOWED) || + IS_BIT_SET(DesiredAccess, SYNCHRONIZE) || + IS_BIT_SET(DesiredAccess, ACCESS_SYSTEM_SECURITY) || + IS_BIT_SET(DesiredAccess, GENERIC_ALL) || + IS_BIT_SET(DesiredAccess, KEY_ALL_ACCESS) || + DesiredAccess == 0) + + OperationType |= OP_READ; + + + if ( IS_BIT_SET(DesiredAccess, GENERIC_WRITE) || + IS_BIT_SET(DesiredAccess, KEY_SET_VALUE) || + IS_BIT_SET(DesiredAccess, KEY_CREATE_SUB_KEY) || + IS_BIT_SET(DesiredAccess, KEY_CREATE_LINK) || + IS_BIT_SET(DesiredAccess, WRITE_OWNER) || + IS_BIT_SET(DesiredAccess, WRITE_DAC) || + IS_BIT_SET(DesiredAccess, DELETE) || + IS_BIT_SET(DesiredAccess, MAXIMUM_ALLOWED) || + IS_BIT_SET(DesiredAccess, GENERIC_ALL) || + IS_BIT_SET(DesiredAccess, KEY_ALL_ACCESS) ) + + OperationType |= OP_WRITE; + + + if ( IS_BIT_SET(DesiredAccess, GENERIC_EXECUTE) || + IS_BIT_SET(DesiredAccess, KEY_NOTIFY) || + IS_BIT_SET(DesiredAccess, MAXIMUM_ALLOWED) || + IS_BIT_SET(DesiredAccess, GENERIC_ALL) || + IS_BIT_SET(DesiredAccess, KEY_ALL_ACCESS) ) + + OperationType |= OP_EXECUTE; + + + if (OperationType == 0) +// OperationType = OP_READ | OP_WRITE | OP_EXECUTE; + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("Get_REGISTRY_OperationType: Unknown desired access mask %x\n", DesiredAccess)); + + + return OperationType; +} + + + +/* + * Get_EVENT_OperationType() + * + * Description: + * This function decodes event operation types such as EVENT_QUERY_STATE and GENERIC_WRITE and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_EVENT_OperationType(ACCESS_MASK DesiredAccess) +{ + UCHAR OperationType = 0; + + + if ( IS_BIT_SET(DesiredAccess, EVENT_QUERY_STATE) || + IS_BIT_SET(DesiredAccess, GENERIC_READ) || + IS_BIT_SET(DesiredAccess, STANDARD_RIGHTS_READ) || + IS_BIT_SET(DesiredAccess, SYNCHRONIZE) || + IS_BIT_SET(DesiredAccess, EVENT_ALL_ACCESS) ) + + OperationType |= OP_READ; + + + if ( IS_BIT_SET(DesiredAccess, EVENT_MODIFY_STATE) || + IS_BIT_SET(DesiredAccess, GENERIC_WRITE) || + IS_BIT_SET(DesiredAccess, WRITE_DAC) || + IS_BIT_SET(DesiredAccess, WRITE_OWNER) || + IS_BIT_SET(DesiredAccess, EVENT_ALL_ACCESS) ) + + OperationType |= OP_WRITE; + + + if (OperationType == 0) +// OperationType = OP_READ | OP_WRITE | OP_EXECUTE; + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("Get_EVENT_OperationType: Unknown desired access mask %x\n", DesiredAccess)); + + + return OperationType; +} + + + +/* + * Get_SEMAPHORE_OperationType() + * + * Description: + * This function decodes semaphore operation types such as SEMAPHORE_QUERY_STATE and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_SEMAPHORE_OperationType(ACCESS_MASK DesiredAccess) +{ + UCHAR OperationType = 0; + + + if ( IS_BIT_SET(DesiredAccess, SEMAPHORE_QUERY_STATE) || + IS_BIT_SET(DesiredAccess, GENERIC_READ) || + IS_BIT_SET(DesiredAccess, READ_CONTROL) || + IS_BIT_SET(DesiredAccess, SEMAPHORE_ALL_ACCESS) ) + + OperationType |= OP_READ; + + + if ( IS_BIT_SET(DesiredAccess, SEMAPHORE_MODIFY_STATE) || + IS_BIT_SET(DesiredAccess, GENERIC_WRITE) || + IS_BIT_SET(DesiredAccess, GENERIC_ALL) || + IS_BIT_SET(DesiredAccess, WRITE_DAC) || + IS_BIT_SET(DesiredAccess, SEMAPHORE_ALL_ACCESS) ) + + OperationType |= OP_WRITE; + + + if (OperationType == 0) +// OperationType = OP_READ | OP_WRITE | OP_EXECUTE; + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("Get_SEMAPHORE_OperationType: Unknown desired access mask %x\n", DesiredAccess)); + + + return OperationType; +} + + + +/* + * Get_SECTION_OperationType() + * + * Description: + * This function decodes section operation types such as SECTION_QUERY and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_SECTION_OperationType(ACCESS_MASK DesiredAccess) +{ + UCHAR OperationType = 0; + + + if ( IS_BIT_SET(DesiredAccess, SECTION_QUERY) || + IS_BIT_SET(DesiredAccess, GENERIC_READ) || + IS_BIT_SET(DesiredAccess, READ_CONTROL) || + IS_BIT_SET(DesiredAccess, SECTION_MAP_READ) || + IS_BIT_SET(DesiredAccess, SECTION_ALL_ACCESS) ) + + OperationType |= OP_READ; + + + if ( IS_BIT_SET(DesiredAccess, SECTION_EXTEND_SIZE) || + IS_BIT_SET(DesiredAccess, GENERIC_WRITE) || + IS_BIT_SET(DesiredAccess, WRITE_DAC) || + IS_BIT_SET(DesiredAccess, GENERIC_ALL) || + IS_BIT_SET(DesiredAccess, SECTION_MAP_WRITE) || + IS_BIT_SET(DesiredAccess, SECTION_ALL_ACCESS) ) + + OperationType |= OP_WRITE; + + + if ( IS_BIT_SET(DesiredAccess, SECTION_MAP_EXECUTE) || + IS_BIT_SET(DesiredAccess, GENERIC_ALL) || + IS_BIT_SET(DesiredAccess, SECTION_ALL_ACCESS) ) + + OperationType |= OP_EXECUTE; + + + if (OperationType == 0) +// OperationType = OP_READ | OP_WRITE | OP_EXECUTE; + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("Get_SECTION_OperationType: Unknown desired access mask %x\n", DesiredAccess)); + + + return OperationType; +} + + + +/* + * Get_JOB_OperationType() + * + * Description: + * This function decodes job object operation types such as JOB_OBJECT_QUERY and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_JOB_OperationType(ACCESS_MASK DesiredAccess) +{ + UCHAR OperationType = 0; + + + if ( IS_BIT_SET(DesiredAccess, JOB_OBJECT_QUERY) || + IS_BIT_SET(DesiredAccess, JOB_OBJECT_ALL_ACCESS) ) + + OperationType |= OP_READ; + + + if ( IS_BIT_SET(DesiredAccess, JOB_OBJECT_ASSIGN_PROCESS) || + IS_BIT_SET(DesiredAccess, JOB_OBJECT_SET_ATTRIBUTES) || + IS_BIT_SET(DesiredAccess, JOB_OBJECT_TERMINATE) || + IS_BIT_SET(DesiredAccess, JOB_OBJECT_SET_SECURITY_ATTRIBUTES) || + IS_BIT_SET(DesiredAccess, JOB_OBJECT_ALL_ACCESS) ) + + OperationType |= OP_WRITE; + + + if (OperationType == 0) +// OperationType = OP_READ | OP_WRITE | OP_EXECUTE; + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("Get_JOB_OperationType: Unknown desired access mask %x\n", DesiredAccess)); + + + return OperationType; +} + + + +/* + * Get_MUTANT_OperationType() + * + * Description: + * This function decodes mutant operation types such as JOB_OBJECT_QUERY and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_MUTANT_OperationType(ACCESS_MASK DesiredAccess) +{ + UCHAR OperationType = 0; + + + if ( IS_BIT_SET(DesiredAccess, MUTANT_QUERY_STATE) || + IS_BIT_SET(DesiredAccess, GENERIC_READ) || + IS_BIT_SET(DesiredAccess, GENERIC_ALL) || + IS_BIT_SET(DesiredAccess, READ_CONTROL) || + IS_BIT_SET(DesiredAccess, SYNCHRONIZE) || + IS_BIT_SET(DesiredAccess, MUTANT_ALL_ACCESS) ) + + OperationType |= OP_READ; + + + if ( IS_BIT_SET(DesiredAccess, MUTANT_ALL_ACCESS) || + IS_BIT_SET(DesiredAccess, WRITE_OWNER) || + IS_BIT_SET(DesiredAccess, GENERIC_WRITE) || + IS_BIT_SET(DesiredAccess, WRITE_DAC) || + IS_BIT_SET(DesiredAccess, GENERIC_ALL) ) + + OperationType |= OP_WRITE; + + + if (OperationType == 0) +// OperationType = OP_READ | OP_WRITE | OP_EXECUTE; + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("Get_MUTANT_OperationType: Unknown desired access mask %x\n", DesiredAccess)); + + + return OperationType; +} + + + +/* + * Get_SYMLINK_OperationType() + * + * Description: + * This function decodes symbolic link operation types such as SYMBOLIC_LINK_QUERY and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_SYMLINK_OperationType(ACCESS_MASK DesiredAccess) +{ + UCHAR OperationType = 0; + + + if ( IS_BIT_SET(DesiredAccess, SYMBOLIC_LINK_QUERY) || + IS_BIT_SET(DesiredAccess, GENERIC_READ) || + IS_BIT_SET(DesiredAccess, SYNCHRONIZE) || + IS_BIT_SET(DesiredAccess, SYMBOLIC_LINK_ALL_ACCESS) ) + + OperationType |= OP_READ; + + + if ( IS_BIT_SET(DesiredAccess, DELETE) || + IS_BIT_SET(DesiredAccess, MAXIMUM_ALLOWED) || + IS_BIT_SET(DesiredAccess, SYMBOLIC_LINK_ALL_ACCESS) ) + + OperationType |= OP_WRITE; + + + if (OperationType == 0) +// OperationType = OP_READ | OP_WRITE | OP_EXECUTE; + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("Get_SYMLINK_OperationType: Unknown desired access mask %x\n", DesiredAccess)); + + + return OperationType; +} + + + +/* + * Get_TIMER_OperationType() + * + * Description: + * This function decodes timer operation types such as JOB_OBJECT_QUERY and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_TIMER_OperationType(ACCESS_MASK DesiredAccess) +{ + UCHAR OperationType = 0; + + + if ( IS_BIT_SET(DesiredAccess, TIMER_QUERY_STATE) || + IS_BIT_SET(DesiredAccess, TIMER_ALL_ACCESS) ) + + OperationType |= OP_READ; + + + if ( IS_BIT_SET(DesiredAccess, TIMER_MODIFY_STATE) || + IS_BIT_SET(DesiredAccess, TIMER_ALL_ACCESS) ) + + OperationType |= OP_WRITE; + + + if (OperationType == 0) +// OperationType = OP_READ | OP_WRITE | OP_EXECUTE; + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("Get_TIMER_OperationType: Unknown desired access mask %x\n", DesiredAccess)); + + + return OperationType; +} + + + +/* + * Get_PORT_OperationType() + * + * Description: + * This function decodes port operation types and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * OP_WRITE. + */ + +UCHAR +Get_PORT_OperationType(ACCESS_MASK DesiredAccess) +{ + return OP_WRITE; +} + + + +/* + * Get_DIROBJ_OperationType() + * + * Description: + * This function decodes directory operation types such as DIRECTORY_QUERY and converts them to + * 3 internal operations: OP_READ, OP_WRITE and OP_EXECUTE. + * + * Parameters: + * DesiredAccess - ACCESS_MASK structure (a doubleword value containing standard, specific, and generic rights). + * + * Returns: + * A combination of OP_READ, OP_WRITE & OP_EXECUTE flags set depending on the DesiredAccess argument. + */ + +UCHAR +Get_DIROBJ_OperationType(ACCESS_MASK DesiredAccess) +{ + UCHAR OperationType = 0; + + + if ( IS_BIT_SET(DesiredAccess, DIRECTORY_QUERY) || + IS_BIT_SET(DesiredAccess, DIRECTORY_TRAVERSE) || + IS_BIT_SET(DesiredAccess, DIRECTORY_ALL_ACCESS) ) + + OperationType |= OP_READ; + + + if ( IS_BIT_SET(DesiredAccess, DIRECTORY_CREATE_OBJECT) || + IS_BIT_SET(DesiredAccess, DIRECTORY_CREATE_SUBDIRECTORY) || + IS_BIT_SET(DesiredAccess, DIRECTORY_ALL_ACCESS) ) + + OperationType |= OP_WRITE; + + + if (OperationType == 0) +// OperationType = OP_READ | OP_WRITE | OP_EXECUTE; + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("Get_DIROBJ_OperationType: Unknown desired access mask %x\n", DesiredAccess)); + + + return OperationType; +} + + + diff --git a/accessmask.h b/accessmask.h new file mode 100644 index 0000000..7ff4a49 --- /dev/null +++ b/accessmask.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * accessmask.h + * + * Abstract: + * + * This module implements various ACCESS_MASK decoding routines. + * + * Author: + * + * Eugene Tsyrklevich 18-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __ACCESSMASK_H__ +#define __ACCESSMASK_H__ + + +#include +#include "policy.h" +#include "ntproto.h" +#include "log.h" + + +// IBS = Is Bit Set? + +#define IS_BIT_SET(da, mask) (((da) & (mask)) == (mask)) + + +UCHAR Get_FILE_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_NAMEDPIPE_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_MAILSLOT_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_REGISTRY_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_EVENT_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_SEMAPHORE_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_SECTION_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_JOB_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_MUTANT_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_SYMLINK_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_TIMER_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_PORT_OperationType(ACCESS_MASK DesiredAccess); +UCHAR Get_DIROBJ_OperationType(ACCESS_MASK DesiredAccess); + +void DecodeFileOperationType(ACCESS_MASK DesiredAccess); + + + +#endif /* __ACCESSMASK_H__ */ \ No newline at end of file diff --git a/atom.c b/atom.c new file mode 100644 index 0000000..91ecc98 --- /dev/null +++ b/atom.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * atom.c + * + * Abstract: + * + * This module implements various atom hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include "atom.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitAtomHooks) +#endif + + +fpZwAddAtom OriginalNtAddAtom = NULL; +fpZwFindAtom OriginalNtFindAtom = NULL; + + + +/* + * HookedNtCreateAtom() + * + * Description: + * This function mediates the NtAddAtom() system service and checks the + * provided atom name against the global and current process security policies. + * + * NOTE: ZwAddAtom adds an atom to the global atom table. [NAR] + * + * Parameters: + * Those of NtAddAtom(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtAddAtom(). + */ + +NTSTATUS +NTAPI +HookedNtAddAtom +( + IN PWSTR String, + IN ULONG StringLength, + OUT PUSHORT Atom +) +{ + PCHAR FunctionName = "HookedNtAddAtom"; + CHAR ATOMNAME[MAX_PATH]; + + + HOOK_ROUTINE_ENTER(); + + + if (!VerifyPwstr(String, StringLength)) + { + LOG(LOG_SS_ATOM, LOG_PRIORITY_DEBUG, ("HookedNtAddAtom: VerifyPwstr(%x) failed\n", String)); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + _snprintf(ATOMNAME, MAX_PATH, "%S", String); + ATOMNAME[ MAX_PATH - 1 ] = 0; + + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(ATOM, OP_WRITE); + } + + + ASSERT(OriginalNtAddAtom); + + rc = OriginalNtAddAtom(String, StringLength, Atom); + + + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(ATOM, ATOMNAME, OP_WRITE); +} + + + +/* + * HookedNtFindAtom() + * + * Description: + * This function mediates the NtFindAtom() system service and checks the + * provided atom name against the global and current process security policies. + * + * NOTE: ZwFindAtom searches for an atom in the global atom table. [NAR] + * + * Parameters: + * Those of NtFindAtom(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtFindAtom(). + */ + +NTSTATUS +NTAPI +HookedNtFindAtom +( + IN PWSTR String, + IN ULONG StringLength, + OUT PUSHORT Atom +) +{ + PCHAR FunctionName = "HookedNtFindAtom"; + CHAR ATOMNAME[MAX_PATH]; + + + HOOK_ROUTINE_ENTER(); + + + if (!VerifyPwstr(String, StringLength)) + { + LOG(LOG_SS_ATOM, LOG_PRIORITY_DEBUG, ("HookedNtFindAtom: VerifyPwstr(%x) failed\n", String)); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + _snprintf(ATOMNAME, MAX_PATH, "%S", String); + ATOMNAME[ MAX_PATH - 1 ] = 0; + + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(ATOM, OP_READ); + } + + + ASSERT(OriginalNtFindAtom); + + rc = OriginalNtFindAtom(String, StringLength, Atom); + + + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(ATOM, ATOMNAME, OP_READ); +} + + + +/* + * InitAtomHooks() + * + * Description: + * Initializes all the mediated atom operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitAtomHooks() +{ + if ( (OriginalNtAddAtom = (fpZwAddAtom) ZwCalls[ZW_ADD_ATOM_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_ATOM, LOG_PRIORITY_DEBUG, ("InitAtomHooks: OriginalNtAddAtom is NULL\n")); + return FALSE; + } + + if ( (OriginalNtFindAtom = (fpZwFindAtom) ZwCalls[ZW_FIND_ATOM_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_ATOM, LOG_PRIORITY_DEBUG, ("InitAtomHooks: OriginalNtFindAtom is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/atom.h b/atom.h new file mode 100644 index 0000000..68af7f8 --- /dev/null +++ b/atom.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * atom.h + * + * Abstract: + * + * This module defines various types used by atom object hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 06-Apr-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __ATOM_H__ +#define __ATOM_H__ + + +#include +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" +#include "log.h" + + +/* + * ZwAddAtom adds an atom to the global atom table. [NAR] + */ + +typedef NTSTATUS (*fpZwAddAtom) ( + IN PWSTR String, + IN ULONG StringLength, + OUT PUSHORT Atom + ); + +NTSTATUS +NTAPI +HookedNtAddAtom( + IN PWSTR String, + IN ULONG StringLength, + OUT PUSHORT Atom + ); + + + +/* + * ZwFindAtom searches for an atom in the global atom table. [NAR] + */ + + +typedef NTSTATUS (*fpZwFindAtom) ( + IN PWSTR String, + IN ULONG StringLength, + OUT PUSHORT Atom + ); + +NTSTATUS +NTAPI +HookedNtFindAtom( + IN PWSTR String, + IN ULONG StringLength, + OUT PUSHORT Atom + ); + + +BOOLEAN InitAtomHooks(); + + +#endif /* __ATOM_H__ */ diff --git a/boprot.c b/boprot.c new file mode 100644 index 0000000..b490012 --- /dev/null +++ b/boprot.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * boport.c + * + * Abstract: + * + * This module implements buffer overflow protection related routines. + * Specifically, kernel32.dll randomization. The rest of buffer overflow + * code is in process.c + * + * Author: + * + * Eugene Tsyrklevich 08-Jun-2004 + * + * Revision History: + * + * None. + */ + + +#include "boprot.h" +#include "hookproc.h" +#include "i386.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitBufferOverflowProtection) +#endif + + +ULONG Kernel32Offset = 0, User32Offset = 0; + + +/* + * InitBufferOverflowProtection() + * + * Description: + * . + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitBufferOverflowProtection() +{ + ULONG addr; + + + if (NTDLL_Base == NULL) + return FALSE; + + + __try + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("searching for kernel32.dll (%x)\n", NTDLL_Base)); + for (addr = (ULONG) NTDLL_Base; addr < 0x77ff9fff; addr++) + { + if (_wcsnicmp((PWSTR) addr, L"kernel32.dll", 12) == 0) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("InitBufferOverflowProtection: found kernel32.dll string at offset %x\n", addr)); + Kernel32Offset = addr; + if (User32Offset) + break; + } + + if (_wcsnicmp((PWSTR) addr, L"user32.dll", 12) == 0) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("InitBufferOverflowProtection: found user32.dll string at offset %x\n", addr)); + User32Offset = addr; + if (Kernel32Offset) + break; + } + } + + /* kernel32.dll and user32.dll strings are supposed to follow each other */ + if (!Kernel32Offset || !User32Offset || (abs(User32Offset - Kernel32Offset) > 32)) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_WARNING, ("InitBufferOverflowProtection: incorrect kernel32.dll (%x) and user32.dll (%x) offsets\n", Kernel32Offset, User32Offset)); + Kernel32Offset = 0; + User32Offset = 0; + return FALSE; + } + + if (Kernel32Offset) + { +//XXX convert to use Mdl routines + INTERRUPTS_OFF(); + MEMORY_PROTECTION_OFF(); + + /* overwrite the first WCHAR of L"kernel32.dll" string with a zero */ + * (PWCHAR) Kernel32Offset = 0; + + MEMORY_PROTECTION_ON(); + INTERRUPTS_ON(); + } +#if 0 + if (User32Offset) + { + INTERRUPTS_OFF(); + MEMORY_PROTECTION_OFF(); + + * (PWCHAR) User32Offset = 0; + + MEMORY_PROTECTION_ON(); + INTERRUPTS_ON(); + } +#endif + + } // __try + + __except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + LOG(LOG_SS_MISC, LOG_PRIORITY_WARNING, ("InitBufferOverflowProtection: caught an exception. status = 0x%x\n", status)); + + return FALSE; + } + + + return TRUE; +} + + + +/* + * ShutdownBufferOverflowProtection() + * + * Description: + * . + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +VOID +ShutdownBufferOverflowProtection() +{ + if (Kernel32Offset) + { + INTERRUPTS_OFF(); + MEMORY_PROTECTION_OFF(); + + /* restore the first WCHAR of L"kernel32.dll" string */ + * (PWCHAR) Kernel32Offset = L'k'; + + MEMORY_PROTECTION_ON(); + INTERRUPTS_ON(); + } +#if 0 + if (User32Offset) + { + INTERRUPTS_OFF(); + MEMORY_PROTECTION_OFF(); + + * (PWCHAR) User32Offset = L'u'; + + MEMORY_PROTECTION_ON(); + INTERRUPTS_ON(); + } +#endif +} diff --git a/boprot.h b/boprot.h new file mode 100644 index 0000000..3c106a6 --- /dev/null +++ b/boprot.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * boBOPROT.c + * + * Abstract: + * + * This module implements buffer overflow protection related routines. + * + * Author: + * + * Eugene Tsyrklevich 08-Jun-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __BOPROT_H__ +#define __BOPROT_H__ + + +#include + + +BOOLEAN InitBufferOverflowProtection(); +VOID ShutdownBufferOverflowProtection(); + + +#endif /* __BOPROT_H__ */ diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..e48a9eb --- /dev/null +++ b/debug.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * debug.c + * + * Abstract: + * + * This module implements various debug hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 23-Apr-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "debug.h" +#include "hookproc.h" +#include "procname.h" +#include "learn.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitDebugHooks) +#endif + + +fpZwDebugActiveProcess OriginalNtDebugActiveProcess = NULL; + + +//XXX http://www.nsfocus.net/index.php?act=magazine&do=view&mid=2108 + + +/* + * IsDebuggingAllowed() + * + * Description: + * Check whether the current process is allowed to use debugging functionality. + * + * Parameters: + * None. + * + * Returns: + * FALSE if debugging is disabled. TRUE otherwise. + */ + +BOOLEAN +IsDebuggingAllowed() +{ + PIMAGE_PID_ENTRY CurrentProcess; + BOOLEAN DebuggingAllowed = FALSE; + + + /* check the global policy first */ + if (! IS_DEBUGGING_PROTECTION_ON(gSecPolicy)) + return TRUE; + + + /* now check the process specific policy */ + CurrentProcess = FindImagePidEntry(CURRENT_PROCESS_PID, 0); + + if (CurrentProcess != NULL) + { + DebuggingAllowed = ! IS_DEBUGGING_PROTECTION_ON(CurrentProcess->SecPolicy); + } + else + { + LOG(LOG_SS_DEBUG, LOG_PRIORITY_DEBUG, ("%d IsDebuggingAllowed: CurrentProcess = NULL!\n", CURRENT_PROCESS_PID)); + } + + + return DebuggingAllowed; +} + + + +/* + * HookedNtDebugActiveProcess() + * + * Description: + * This function mediates the NtDebugActiveProcess() system service and disallows + * debugging. + * + * Parameters: + * Those of NtDebugActiveProcess(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtDebugActiveProcess(). + */ + +NTSTATUS +NTAPI +HookedNtDebugActiveProcess +( + UINT32 Unknown1, + UINT32 Unknown2 +) +{ + HOOK_ROUTINE_ENTER(); + + + LOG(LOG_SS_DEBUG, LOG_PRIORITY_DEBUG, ("HookedNtDebugActiveProcess(%x %x)\n", Unknown1, Unknown2)); + + if (LearningMode == FALSE && IsDebuggingAllowed() == FALSE) + { + LOG(LOG_SS_DEBUG, LOG_PRIORITY_DEBUG, ("%d (%S) HookedNtDebugActiveProcess: disallowing debugging\n", (ULONG) PsGetCurrentProcessId(), GetCurrentProcessName())); + + LogAlert(ALERT_SS_DEBUG, OP_DEBUG, ALERT_RULE_NONE, ACTION_DENY, ALERT_PRIORITY_MEDIUM, NULL, 0, NULL); + + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + ASSERT(OriginalNtDebugActiveProcess); + + rc = OriginalNtDebugActiveProcess(Unknown1, Unknown2); + + + if (LearningMode == TRUE) + TURN_DEBUGGING_PROTECTION_OFF(NewPolicy); + + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * InitDebugHooks() + * + * Description: + * Initializes all the mediated debug operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitDebugHooks() +{ + if ( (OriginalNtDebugActiveProcess = (fpZwDebugActiveProcess) ZwCalls[ZW_DEBUG_ACTIVEPROCESS_INDEX].OriginalFunction) == NULL) + { + /* does not exist on Win2K */ + LOG(LOG_SS_DEBUG, LOG_PRIORITY_DEBUG, ("InitDebugHooks: OriginalNtDebugActiveProcess is NULL\n")); + } + + return TRUE; +} diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..b2f9077 --- /dev/null +++ b/debug.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * debug.h + * + * Abstract: + * + * This module implements various debug hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 23-Apr-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + + + +/* + * ZwDebugActiveProcess. + */ + +typedef NTSTATUS (*fpZwDebugActiveProcess) ( + UINT32 Unknown1, + UINT32 Unknown2 + ); + +NTSTATUS +NTAPI +HookedNtDebugActiveProcess( + UINT32 Unknown1, + UINT32 Unknown2 + ); + + +BOOLEAN InitDebugHooks(); + + +#endif /* __DEBUG_H__ */ diff --git a/dirobj.c b/dirobj.c new file mode 100644 index 0000000..b925e42 --- /dev/null +++ b/dirobj.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * dirobj.c + * + * Abstract: + * + * This module implements various object directory hooking routines. + * These are not file system directories (see file.c) but rather containers + * for other objects. + * + * Author: + * + * Eugene Tsyrklevich 03-Sep-2004 + * + * Revision History: + * + * None. + */ + + +#include "dirobj.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitDirobjHooks) +#endif + + +fpZwOpenDirectoryObject OriginalNtOpenDirectoryObject = NULL; +fpZwCreateDirectoryObject OriginalNtCreateDirectoryObject = NULL; + + + +/* + * HookedNtCreateDirectoryObject() + * + * Description: + * This function mediates the NtCreateDirectoryObject() system service and checks the + * provided directory object name against the global and current process security policies. + * + * NOTE: ZwCreateDirectoryObject creates or opens an object directory. [NAR] + * + * Parameters: + * Those of NtCreateDirectoryObject(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateDirectoryObject(). + */ + +NTSTATUS +NTAPI +HookedNtCreateDirectoryObject +( + OUT PHANDLE DirectoryHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtCreateDirectoryObject"; + + + HOOK_ROUTINE_START(DIROBJ); + + + ASSERT(OriginalNtCreateDirectoryObject); + + rc = OriginalNtCreateDirectoryObject(DirectoryHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(DIROBJ); +} + + + +/* + * HookedNtOpenDirectoryObject() + * + * Description: + * This function mediates the NtOpenDirectoryObject() system service and checks the + * provided directory object name against the global and current process security policies. + * + * NOTE: ZwOpenDirectoryObject opens an object directory. [NAR] + * + * Parameters: + * Those of NtOpenDirectoryObject(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtOpenDirectoryObject(). + */ + +NTSTATUS +NTAPI +HookedNtOpenDirectoryObject +( + OUT PHANDLE DirectoryHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtOpenDirectoryObject"; + + + HOOK_ROUTINE_START(DIROBJ); + + + ASSERT(OriginalNtOpenDirectoryObject); + + rc = OriginalNtOpenDirectoryObject(DirectoryHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(DIROBJ); +} + + + +/* + * InitDirobjHooks() + * + * Description: + * Initializes all the mediated driver object operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitDirobjHooks() +{ + if ( (OriginalNtOpenDirectoryObject = (fpZwOpenDirectoryObject) ZwCalls[ZW_OPEN_DIRECTORYOBJECT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("InitDirobjHooks: OriginalNtOpenDirectoryObject is NULL\n")); + return FALSE; + } + + if ( (OriginalNtCreateDirectoryObject = (fpZwCreateDirectoryObject) ZwCalls[ZW_CREATE_DIRECTORYOBJECT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("InitDirobjHooks: OriginalNtCreateDirectoryObject is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/dirobj.h b/dirobj.h new file mode 100644 index 0000000..311402e --- /dev/null +++ b/dirobj.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * dirobj.h + * + * Abstract: + * + * This module defines various types used by object directory hooking routines. + * These are not file system directories (see file.c) but rather containers + * for other objects. + * + * Author: + * + * Eugene Tsyrklevich 03-Sep-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __DIROBJ_H__ +#define __DIROBJ_H__ + + +#include +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" +#include "log.h" + + +/* + * ZwCreateDirectoryObject creates or opens an object directory. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateDirectoryObject) ( + OUT PHANDLE DirectoryHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS +NTAPI +HookedNtCreateDirectoryObject( + OUT PHANDLE DirectoryHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +/* + * ZwOpenDirectoryObject opens an object directory. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenDirectoryObject) ( + OUT PHANDLE DirectoryHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS +NTAPI +HookedNtOpenDirectoryObject( + OUT PHANDLE DirectoryHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +BOOLEAN InitDirobjHooks(); + + +#endif /* __DIROBJ_H__ */ diff --git a/driver.c b/driver.c new file mode 100644 index 0000000..09f66ad --- /dev/null +++ b/driver.c @@ -0,0 +1,1237 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * driver.c + * + * Abstract: + * + * This module implements all the device driver "plumbing" (DriverEntry, etc). + * + * Author: + * + * Eugene Tsyrklevich 9-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include + +#include "driver.h" +//#include "eugene.h" +#include "file.h" +#include "registry.h" +#include "sysinfo.h" +#include "policy.h" +#include "process.h" +#include "learn.h" +#include "event.h" +#include "semaphore.h" +#include "dirobj.h" +#include "symlink.h" +#include "mutant.h" +#include "port.h" +#include "timer.h" +#include "token.h" +#include "job.h" +#include "driverobj.h" +#include "network.h" +#include "section.h" +#include "atom.h" +#include "time.h" +#include "vdm.h" +#include "procname.h" +#include "userland.h" +#include "media.h" +#include "boprot.h" +#include "debug.h" +#include "i386.h" +#include "misc.h" +#include "log.h" + + +LONG SysenterEip = 0; + + +__declspec(naked) +VOID +SysenterHandler() +{ + _asm and ecx, 0x000000FF + _asm sub esp, ecx + _asm jmp [SysenterEip] +} + + +VOID +blah() +{ + LONG c, s; + +#define SYSENTER_CS_MSR 0x174 +#define SYSENTER_ESP_MSR 0x175 +#define SYSENTER_EIP_MSR 0x176 + + _asm + { + mov ecx, SYSENTER_CS_MSR + rdmsr + + mov c, eax + + + mov ecx, SYSENTER_ESP_MSR + rdmsr + + mov s, eax + + + mov ecx, SYSENTER_EIP_MSR + rdmsr + + mov SysenterEip, eax + + mov eax, SysenterHandler + + wrmsr + } + + KdPrint(("old eip=%x:%x (%x), new eip=%x\n", c, SysenterEip, s, SysenterHandler)); +} + + + +/* + * DriverEntry() + * + * Description: + * Driver entry point. + * + * Parameters: + * pDriverObject - pointer to an initialized driver object that represents our driver + * pRegistryPath - name of the service key in the registry + * + * Returns: + * STATUS_SUCCESS to indicate success or an error code to indicate an error. + */ + +/* macro shortcut for bailing out of DriverEntry in case of an error */ + +#define ABORT_DriverEntry(msg) \ + { \ + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_CRITICAL, (msg)); \ + if (irql != PASSIVE_LEVEL) KeLowerIrql(irql); \ + DriverUnload(pDriverObject); \ + return status; \ + } +/* +PVOID Find_Kernel32_Base(); +NTSTATUS +NTAPI +ZwCreateSymbolicLinkObject( + OUT PHANDLE SymbolicLinkHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PUNICODE_STRING TargetName + ); + HANDLE h; +*/ +NTSTATUS +DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath) +{ + UNICODE_STRING usDeviceName, usSymLinkName; + PDEVICE_OBJECT pDeviceObject; + PDEVICE_EXTENSION pDeviceExtension; + NTSTATUS status; + KIRQL irql = KeGetCurrentIrql(); + int i; + +/* + { + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING dest, target; + NTSTATUS status; + + RtlInitUnicodeString(&dest, L"\\??\\MyRegistryMachine"); + RtlInitUnicodeString(&target, L"\\Registry\\Machine"); + + InitializeObjectAttributes(&ObjectAttributes, &dest, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + status = ZwCreateSymbolicLinkObject(&h, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &target); + if (! NT_SUCCESS(status)) + { + KdPrint(("failed, status %x\n", status)); + } + else + { + KdPrint(("link ok\n")); + } + +// return STATUS_UNSUCCESSFUL; + } +*/ + //XXX add pRegistryPath to deny registry access rule?! + + __try + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverEntry: Entered (%x %S)\n", pDriverObject, pRegistryPath->Buffer)); + + +// blah(); +// KdPrint(("after blah\n")); + + + /* + * Verify we are running on x86 & everything is in order + */ + + if (!InitI386()) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_CRITICAL, ("InitI386 failed. Aborting.\n")); + return STATUS_UNSUCCESSFUL; + } + + + /* + * Initialize all the driver object related data and create a device representing our device + */ + + // set to NULL to disable unload by admins +// pDriverObject->DriverUnload = NULL; + pDriverObject->DriverUnload = DriverUnload; + +//XXX need to intercept DriverObject->FastIoDispatch = &VTrcFSFastIoDispatchTable; + + for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) + //XXX don't intercept IRP_MJ_POWER & IRP_MJ_PNP + pDriverObject->MajorFunction[i] = DriverDeviceControl; + + pDriverObject->MajorFunction[ IRP_MJ_CREATE ] = DriverCreate; + pDriverObject->MajorFunction[ IRP_MJ_CLEANUP ] = DriverCleanup; + pDriverObject->MajorFunction[ IRP_MJ_CLOSE ] = DriverClose; + pDriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = DriverDeviceControl; +/* + pDriverObject->MajorFunction[ IRP_MJ_READ ] = DriverRead; + pDriverObject->MajorFunction[ IRP_MJ_WRITE ] = DriverWrite; + */ + RtlInitUnicodeString(&usDeviceName, DEVICE_NAME); + + status = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENSION), + &usDeviceName, FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, FALSE,//FALSE (Exclusive - Reserved for system use. Drivers set this parameter to FALSE.) +// 0, TRUE, + &pDeviceObject); + + if (!NT_SUCCESS(status)) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverEntry: IoCreateDevice failed with status %x\n", status)); + + return status; + } + + + pDeviceObject->Flags |= DO_BUFFERED_IO; + + pDeviceExtension = (PDEVICE_EXTENSION) pDeviceObject->DeviceExtension; + pDeviceExtension->pDeviceObject = pDeviceObject; + + + RtlInitUnicodeString(&pDeviceExtension->usSymLink, DEVICE_SYMLINK_NAME); + + status = IoCreateSymbolicLink(&pDeviceExtension->usSymLink, &usDeviceName); + if (!NT_SUCCESS(status)) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverEntry: IoCreateSymbolicLink failed with status %x\n", status)); + + IoDeleteDevice(pDeviceObject); + + return status; + } + + + /* + * Now, mediate all the necessary calls + */ + +#if HOOK_NETWORK + status = InstallNetworkHooks(pDriverObject); + if (! NT_SUCCESS(status)) + ABORT_DriverEntry("InstallNetworkHooks() failed"); +#endif + + + /* all consequitive calls that fail will cause the following error to be returned */ + + status = STATUS_DRIVER_INTERNAL_ERROR; + + if (!InitSyscallsHooks()) + ABORT_DriverEntry("InitSyscallsHooks() failed\n"); + + + /* + * raise irql to DPC level to avoid any spurious system calls taking place before we + * manage to initialize appropriate hooked function pointers + */ + + irql = KeRaiseIrqlToDpcLevel(); + + + if (!InstallSyscallsHooks()) + ABORT_DriverEntry("InstallSyscallsHooks() failed\n"); + + +#if HOOK_FILE + if (!InitFileHooks()) + ABORT_DriverEntry("InitFileHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitFileHooks\n")); +#endif + +#if HOOK_REGISTRY + if (!InitRegistryHooks()) + ABORT_DriverEntry("InitRegistryHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitRegistryHooks\n")); +#endif + +#if HOOK_SECTION + if (!InitSectionHooks()) + ABORT_DriverEntry("InitSectionHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitSectionHooks\n")); +#endif + +#if HOOK_SYSINFO + if (!InitSysInfoHooks()) + ABORT_DriverEntry("InitSysInfoHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitSysInfoHooks\n")); +#endif + +#if HOOK_EVENT + if (!InitEventHooks()) + ABORT_DriverEntry("InitEventHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitEventHooks\n")); +#endif + +#if HOOK_SEMAPHORE + if (!InitSemaphoreHooks()) + ABORT_DriverEntry("InitSemaphoreHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitSemaphoreHooks\n")); +#endif + +#if HOOK_JOB + if (!InitJobHooks()) + ABORT_DriverEntry("InitJobHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitJobHooks\n")); +#endif + +#if HOOK_MUTANT + if (!InitMutantHooks()) + ABORT_DriverEntry("InitMutantHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitMutantHooks\n")); +#endif + +#if HOOK_DIROBJ + if (!InitDirobjHooks()) + ABORT_DriverEntry("InitDirobjHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitDirobjHooks\n")); +#endif + +#if HOOK_PORT + if (!InitPortHooks()) + ABORT_DriverEntry("InitPortHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitPortHooks\n")); +#endif + +#if HOOK_SYMLINK + if (!InitSymlinkHooks()) + ABORT_DriverEntry("InitSymlinkHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitSymlinkHooks\n")); +#endif + +#if HOOK_TIMER + if (!InitTimerHooks()) + ABORT_DriverEntry("InitTimerHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitTimerHooks\n")); +#endif + +#if HOOK_TOKEN + if (!InitTokenHooks()) + ABORT_DriverEntry("InitTokenHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitTokenHooks\n")); +#endif + +#if HOOK_TIME + if (!InitTimeHooks()) + ABORT_DriverEntry("InitTimeHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitTimeHooks\n")); +#endif + +#if HOOK_DRIVEROBJ + if (!InitDriverObjectHooks()) + ABORT_DriverEntry("InitDriverObjectHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitDriverObjectHooks\n")); +#endif + +#if HOOK_ATOM + if (!InitAtomHooks()) + ABORT_DriverEntry("InitAtomHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitAtomHooks\n")); +#endif + +#if HOOK_VDM + if (!InitVdmHooks()) + ABORT_DriverEntry("InitVdmHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitVdmHooks\n")); +#endif + + +#if HOOK_DEBUG + if (!InitDebugHooks()) + ABORT_DriverEntry("InitDebugHooks() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitDebugHooks\n")); +#endif + + + KeLowerIrql(irql); + + + /* + * The order of the following calls is important: + * + * InitProcessEntries() initializes OzoneInstallPath + * InitPolicy() initiailizes policy related variables + * InitProcessNameEntries() then uses policy vars & OzoneInstallPath to load policies + */ + +#if HOOK_PROCESS + if (!InitProcessEntries()) + ABORT_DriverEntry("InitProcessEntries() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitProcessEntries\n")); +#endif + + + if (!InitProcessNameEntries()) + ABORT_DriverEntry("InitProcessNameEntries() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitProcessNameEntries\n")); + + + if (!InitPolicy()) + ABORT_DriverEntry("InitPolicy() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitPolicy\n")); + + + EnumerateExistingProcesses(); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past EnumerateExistingProcesses\n")); + + + + if (LearningMode == TRUE) + { + if (!InitLearningMode()) + ABORT_DriverEntry("InitLearningMode() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitLearningMode\n")); + } + + +#if HOOK_MEDIA + if (!InitRemovableMediaHooks(pDriverObject, pDeviceObject)) + ABORT_DriverEntry("InitRemovableMedia() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitRemovableMediaHooks\n")); +#endif + + +#if HOOK_BOPROT + if (!InitBufferOverflowProtection()) + ABORT_DriverEntry("InitBufferOverflowProtection() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitBufferOverflowProtection\n")); +#endif + + + if (!InitLog()) + ABORT_DriverEntry("InitLog() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitLog\n")); + + + if (!InitUserland()) + ABORT_DriverEntry("InitUserland() failed\n"); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverEntry: Past InitUserland\n")); + + } // __try + + __except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_CRITICAL, ("DriverEntry: caught an exception. status = 0x%x\n", status)); + + return STATUS_DRIVER_INTERNAL_ERROR; + } + + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverEntry: Done\n")); + + + return STATUS_SUCCESS; +} + + + +/* + * DriverUnload() + * + * Description: + * Clean up and unload the driver. + * + * NOTE: Since this driver mediates system calls and other devices, it is not safe to + * unload the driver since there might remain outstanding references to our driver code and data + * segments once the driver is unloaded. + * + * NOTE2: In release builds, unload functionality should be disabled for security reasons. + * + * Parameters: + * pDriverObject - pointer to a driver object that represents this driver. + * + * Returns: + * Nothing. + */ + +VOID +DriverUnload(IN PDRIVER_OBJECT pDriverObject) +{ + PDEVICE_OBJECT pDeviceObject, pNextDeviceObject; + PDEVICE_EXTENSION pDeviceExtension; + LARGE_INTEGER delay; + + +#if DBG + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverUnload: irql = %d %d\n", KeGetCurrentIrql(), HookedTDIRunning)); +#endif + + + if (SysenterEip) + _asm + { + mov ecx, SYSENTER_EIP_MSR + mov eax, SysenterEip + xor edx, edx + wrmsr + } + +#if HOOK_BOPROT + ShutdownBufferOverflowProtection(); + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverUnload: Past ShutdownBufferOverflowProtection\n")); +#endif + + + RemoveRemovableMediaHooks(); + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverUnload: Past RemoveRemovableMediaHooks\n")); + + RemoveNetworkHooks(pDriverObject); + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverUnload: Past RemoveNetworkHooks\n")); + + RemoveSyscallsHooks(); + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverUnload: Past RemoveSyscallsHooks\n")); + + RemoveProcessNameEntries(); + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverUnload: Past RemoveProcessNameEntries\n")); + + + if (LearningMode) + ShutdownLearningMode(); + else + PolicyRemove(); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverUnload: Past LearningMode\n")); + + + ShutdownLog(); + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverUnload: Past ShutdownLog\n")); + + + ShutdownUserland(); + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverUnload: Past UserlandShutdown\n")); + + + pDeviceObject = pNextDeviceObject = pDriverObject->DeviceObject; + + while (pNextDeviceObject != NULL) + { + pNextDeviceObject = pDeviceObject->NextDevice; + pDeviceExtension = (PDEVICE_EXTENSION) pDeviceObject->DeviceExtension; + + if (pDeviceExtension) + { + NTSTATUS status; + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverUnload: IoDeleteSymbolicLink(%S)\n", pDeviceExtension->usSymLink.Buffer)); + + status = IoDeleteSymbolicLink(&pDeviceExtension->usSymLink); + if (! NT_SUCCESS(status)) + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverUnload: IoDeleteSymbolicLink failed: %x\n", status)); + } + else + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverUnload: pDeviceExtension = NULL\n")); + + IoDeleteDevice(pDeviceObject); + pDeviceObject = pNextDeviceObject; + } + + + /* wait for 1 second for all timers/callbacks to complete */ + delay.QuadPart = SECONDS(1); + + KeDelayExecutionThread(KernelMode, FALSE, &delay); + + +#if DBG + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverUnload: HookedRoutineRunning = %d\n", HookedRoutineRunning)); +#endif + + + return; +} + + + +/* + * DriverDeviceControl() + * + * Description: + * Dispatch routine. Process network (TDI) and our driver requests. + * + * Parameters: + * pDeviceObject - pointer to a device object that a request is being sent to. + * pIrp - IRP (I/O Request Packet) request. + * + * Returns: + * Nothing. + */ + +#define COMPLETE_REQUEST(irp, status) \ + pIrp->IoStatus.Status = (status); \ + IoCompleteRequest((irp), IO_NO_INCREMENT); \ + return((status)); + +NTSTATUS +DriverDeviceControl(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp) +{ + PIO_STACK_LOCATION pIrpStack; + ULONG ControlCode; + NTSTATUS status; + ULONG InSize, OutSize; + KIRQL irql; + + + if (pDeviceObject == NULL || pIrp == NULL) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: NULL value %x %x\n", pDeviceObject, pIrp)); + + COMPLETE_REQUEST(pIrp, STATUS_UNSUCCESSFUL); + } + + +#if HOOK_NETWORK + if (TDIDispatch(pDeviceObject, pIrp, &status) == TRUE) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverDeviceControl(%x, %x): TDIDispatch\n", pDeviceObject, pIrp)); + return status; + } +#endif + + + pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + + pIrp->IoStatus.Information = 0; + + + ControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode; + InSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; + OutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + + switch (ControlCode) + { + /* + * When userland agent service starts up, it registers with the driver using IOCTL_REGISTER_AGENT_SERVICE. + * Expects back 1 ULONG - version of the driver + */ + + // XXX save agent pid and version? + case IOCTL_REGISTER_AGENT_SERVICE: + { + ULONG DriverVersion = DRIVER_VERSION; + + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_REGISTER_AGENT_SERVICE ControlCode=%x InBufferSize=%x OutBufferSize=%x\n", ControlCode, InSize, OutSize)); + + + if (OutSize < sizeof(ULONG)) + { + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + + RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer, &DriverVersion, sizeof(ULONG)); + + ActiveUserAgent = TRUE; + + + pIrp->IoStatus.Information = sizeof(ULONG); + + status = STATUS_SUCCESS; + + + break; + } + + + /* + * Userland agent service retrieves log alerts using IOCTL_GET_ALERT + */ + + case IOCTL_GET_ALERT: + { + PSECURITY_ALERT TmpAlert; + + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_GET_ALERT ControlCode=%x InBufferSize=%x OutBufferSize=%x\n", ControlCode, InSize, OutSize)); + + +// if (UserAgentRegistered == FALSE) +// XXX; + + KeAcquireSpinLock(&gLogSpinLock, &irql); + { + if (LogList == NULL) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_GET_ALERT No More Alerts\n")); + + status = STATUS_NO_MORE_ENTRIES; + + KeReleaseSpinLock(&gLogSpinLock, irql); + + break; + } + + /* don't count the size of the Next pointer */ + LogList->Size -= sizeof(struct _SECURITY_ALERT *); + + if (OutSize < LogList->Size) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_GET_ALERT %d < %d\n", OutSize, LogList->Size)); + status = STATUS_INVALID_BUFFER_SIZE; + KeReleaseSpinLock(&gLogSpinLock, irql); + break; + } + + /* copy the SECURITY_ALERT structure without including the Next pointer */ + RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer, (PCHAR)LogList + sizeof(struct _SECURITY_ALERT *), LogList->Size); + + pIrp->IoStatus.Information = LogList->Size; + + + --NumberOfAlerts; + + TmpAlert = LogList; + LogList = LogList->Next; + + ExFreePoolWithTag(TmpAlert, _POOL_TAG); + } + KeReleaseSpinLock(&gLogSpinLock, irql); + + + status = STATUS_SUCCESS; + + break; + } + + + /* + * Userland agent service retrieves userland requests using IOCTL_GET_USERLAND_REQUEST + */ + + case IOCTL_GET_USERLAND_REQUEST: + { + PUSERLAND_REQUEST_HEADER TmpRequest; + + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_GET_USERLAND_REQUEST ControlCode=%x InBufferSize=%x OutBufferSize=%x\n", ControlCode, InSize, OutSize)); + + + KeAcquireSpinLock(&gUserlandRequestListSpinLock, &irql); + { + USHORT UserlandRequestSize; + + + if (UserlandRequestList == NULL) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_GET_USERLAND_REQUEST No More Process Requests\n")); + + status = STATUS_NO_MORE_ENTRIES; + + KeReleaseSpinLock(&gUserlandRequestListSpinLock, irql); + + break; + } + + + /* don't count the size of the Next pointer */ + UserlandRequestSize = UserlandRequestList->RequestSize - sizeof(struct _USERLAND_REQUEST *); + + if (OutSize < UserlandRequestSize) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_GET_USERLAND_REQUEST %d < %d\n", OutSize, UserlandRequestSize)); + + KeReleaseSpinLock(&gUserlandRequestListSpinLock, irql); + + status = STATUS_INVALID_BUFFER_SIZE; + + break; + } + + /* copy the PROCESS_REQUEST structure without including the Next pointer */ + RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer, (PCHAR)UserlandRequestList + sizeof(struct _USERLAND_REQUEST *), UserlandRequestSize); + + pIrp->IoStatus.Information = UserlandRequestSize; + + + TmpRequest = UserlandRequestList; + UserlandRequestList = UserlandRequestList->Next; + + ExFreePoolWithTag(TmpRequest, _POOL_TAG); + } + KeReleaseSpinLock(&gUserlandRequestListSpinLock, irql); + + + status = STATUS_SUCCESS; + + break; + } + + + /* + * Userland agent service returns userland replies using IOCTL_SEND_USERLAND_SID_RESOLVE_REPLY + */ + +#define MAXIMUM_USERLAND_REPLY_SIZE 512 + + case IOCTL_SEND_USERLAND_SID_RESOLVE_REPLY: + { + PSID_RESOLVE_REPLY pSidResolveReply; + PIMAGE_PID_ENTRY ProcessEntry; + + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_SEND_USERLAND_SID_RESOLVE_REPLY ControlCode=%x InBufferSize=%x OutBufferSize=%x\n", ControlCode, InSize, OutSize)); + + + if (InSize > MAXIMUM_USERLAND_REPLY_SIZE) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_SEND_USERLAND_SID_RESOLVE_REPLY %d > %d\n", InSize, MAXIMUM_USERLAND_REPLY_SIZE)); + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + pSidResolveReply = ExAllocatePoolWithTag(PagedPool, InSize, _POOL_TAG); + if (pSidResolveReply == NULL) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_SEND_USERLAND_SID_RESOLVE_REPLY out of memory\n")); + status = STATUS_UNSUCCESSFUL; + break; + } + + + RtlCopyMemory(pSidResolveReply, pIrp->AssociatedIrp.SystemBuffer, InSize); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: Received sid resolve reply. insize=%d seq=%d, %S\n", InSize, pSidResolveReply->ReplyHeader.SeqId, pSidResolveReply->UserName)); + + ProcessEntry = FindImagePidEntry(pSidResolveReply->ReplyHeader.ProcessId, 0); + + if (ProcessEntry) + { + if (ProcessEntry->WaitingForUserRequestId == 0) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: Process (pid=%d) is not expecting a user request!\n", pSidResolveReply->ReplyHeader.ProcessId)); + ExFreePoolWithTag(pSidResolveReply, _POOL_TAG); + ProcessEntry->UserlandReply = NULL; + break; + } + + if (ProcessEntry->WaitingForUserRequestId != pSidResolveReply->ReplyHeader.SeqId) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: Process (pid=%d) is expecting to receive sequence id %d. Got %d\n", pSidResolveReply->ReplyHeader.ProcessId, ProcessEntry->WaitingForUserRequestId, pSidResolveReply->ReplyHeader.SeqId)); + ExFreePoolWithTag(pSidResolveReply, _POOL_TAG); + ProcessEntry->UserlandReply = NULL; + break; + } + + + /* deliver the reply */ + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: Waking up process %d\n", pSidResolveReply->ReplyHeader.ProcessId)); + + ProcessEntry->UserlandReply = (PUSERLAND_REPLY_HEADER) pSidResolveReply; + + KeSetEvent(&ProcessEntry->UserlandRequestDoneEvent, IO_NO_INCREMENT, FALSE); + } + else + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: cannot find process with pid=%d\n", pSidResolveReply->ReplyHeader.ProcessId)); + + + status = STATUS_SUCCESS; + + break; + } + + + /* + * Userland agent service returns "ask user" replies using IOCTL_SEND_USERLAND_ASK_USER_REPLY + */ + + case IOCTL_SEND_USERLAND_ASK_USER_REPLY: + { + PASK_USER_REPLY pAskUserReply; + PIMAGE_PID_ENTRY ProcessEntry; + + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_SEND_USERLAND_ASK_USER_REPLY ControlCode=%x InBufferSize=%x OutBufferSize=%x\n", ControlCode, InSize, OutSize)); + + + if (InSize != sizeof(ASK_USER_REPLY)) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_SEND_USERLAND_ASK_USER_REPLY %d != %d\n", InSize, sizeof(ASK_USER_REPLY))); + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + pAskUserReply = ExAllocatePoolWithTag(PagedPool, sizeof(ASK_USER_REPLY), _POOL_TAG); + if (pAskUserReply == NULL) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_SEND_USERLAND_ASK_USER_REPLY out of memory\n")); + status = STATUS_UNSUCCESSFUL; + break; + } + + + RtlCopyMemory(pAskUserReply, pIrp->AssociatedIrp.SystemBuffer, InSize); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: Received ask user reply. insize=%d, action=%d\n", InSize, pAskUserReply->Action)); + + ProcessEntry = FindImagePidEntry(pAskUserReply->ReplyHeader.ProcessId, 0); + + if (ProcessEntry) + { + if (ProcessEntry->WaitingForUserRequestId == 0) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: Process (pid=%d) is not expecting a user request!\n", pAskUserReply->ReplyHeader.ProcessId)); + ExFreePoolWithTag(pAskUserReply, _POOL_TAG); + ProcessEntry->UserlandReply = NULL; + break; + } + + if (ProcessEntry->WaitingForUserRequestId != pAskUserReply->ReplyHeader.SeqId) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: Process (pid=%d) is expecting to receive sequence id %d. Got %d\n", pAskUserReply->ReplyHeader.ProcessId, ProcessEntry->WaitingForUserRequestId, pAskUserReply->ReplyHeader.SeqId)); + ExFreePoolWithTag(pAskUserReply, _POOL_TAG); + ProcessEntry->UserlandReply = NULL; + break; + } + + + /* deliver the reply */ + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: Waking up process %d\n", pAskUserReply->ReplyHeader.ProcessId)); + + ProcessEntry->UserlandReply = (PUSERLAND_REPLY_HEADER) pAskUserReply; + + KeSetEvent(&ProcessEntry->UserlandRequestDoneEvent, IO_NO_INCREMENT, FALSE); + } + else + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("cannot find process with pid=%d\n", pAskUserReply->ReplyHeader.ProcessId)); + + + status = STATUS_SUCCESS; + + break; + } + + + /* + * train.exe puts the driver in learning/training mode using IOCTL_START_CREATE_POLICY + */ + + case IOCTL_START_CREATE_POLICY: + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverDeviceControl: IOCTL_START_CREATE_POLICY ControlCode=%x InBufferSize=%x OutBufferSize=%x\n", ControlCode, InSize, OutSize)); + + + if ((InSize > MAX_PROCESS_NAME * sizeof(WCHAR)) || (InSize % 2)) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_START_CREATE_POLICY Invalid Insize: %d\n", InSize)); + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + status = STATUS_SUCCESS; + + if (LearningMode == TRUE) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_START_CREATE_POLICY Already in Learning Mode\n")); + break; + } + + RtlCopyMemory(ProcessToMonitor, pIrp->AssociatedIrp.SystemBuffer, InSize); + ProcessToMonitor[(InSize / sizeof(WCHAR)) - 1] = 0; + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_START_CREATE_POLICY Learning about '%S'\n", ProcessToMonitor)); + + LearningMode = TRUE; + + InitLearningMode(); + + break; + } + + + /* + * train.exe stops training/learning mode using IOCTL_STOP_CREATE_POLICY + */ + + case IOCTL_STOP_CREATE_POLICY: + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverDeviceControl: IOCTL_STOP_CREATE_POLICY ControlCode=%x InBufferSize=%x OutBufferSize=%x\n", ControlCode, InSize, OutSize)); + + + if ((InSize > MAX_PROCESS_NAME * sizeof(WCHAR)) || (InSize % 2)) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_STOP_CREATE_POLICY Invalid Insize: %d\n", InSize)); + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + status = STATUS_SUCCESS; + + if (LearningMode == FALSE) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_STOP_CREATE_POLICY Not in Learning Mode\n")); + break; + } + +// RtlCopyMemory(ProcessToMonitor, pIrp->AssociatedIrp.SystemBuffer, InSize); +// ProcessToMonitor[(InSize / sizeof(WCHAR)) - 1] = 0; + +// LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_STOP_CREATE_POLICY '%S'\n", ProcessToMonitor)); + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverDeviceControl: IOCTL_STOP_CREATE_POLICY\n")); + + ShutdownLearningMode(); + + LearningMode = FALSE; + + break; + } + + + default: + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("%d DriverDeviceControl default %x %x %x %x\n", (ULONG) PsGetCurrentProcessId(), pIrpStack->MajorFunction, ControlCode, InSize, OutSize)); + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + + COMPLETE_REQUEST(pIrp, status); +} + + + +NTSTATUS +DriverCreate(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp) +{ + NTSTATUS status; + + +#if HOOK_NETWORK + if (TDIDispatch(pDeviceObject, pIrp, &status) == TRUE) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverCreate(%x, %x): TDIDispatch\n", pDeviceObject, pIrp)); + return status; + } +#endif + + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverCreate(%x, %x)\n", pDeviceObject, pIrp)); + + + //XXX need to consider any possible lock out issues where a valid userland agent is disallowed access + //can verify userland binary name as well +#if 0 + if (ActiveUserAgent == TRUE) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("Userland agent already exists!\n")); + + pIrp->IoStatus.Status = STATUS_ACCESS_DENIED; + pIrp->IoStatus.Information = 0; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return STATUS_ACCESS_DENIED; + } + + ActiveUserAgent = TRUE; +#endif + + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = 0; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + + return STATUS_SUCCESS; +} + + + +NTSTATUS +DriverClose(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp) +{ + NTSTATUS status; + + +#if HOOK_NETWORK + if (TDIDispatch(pDeviceObject, pIrp, &status) == TRUE) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverClose(%x, %x): TDIDispatch\n", pDeviceObject, pIrp)); + return status; + } +#endif + + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverClose(%x, %x)\n", pDeviceObject, pIrp)); + +#if 0 + if (ActiveUserAgent == FALSE) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("Userland agent does not exist!\n")); + } + + ActiveUserAgent = FALSE; +#endif + + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = 0; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + + +NTSTATUS +DriverCleanup(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp) +{ + NTSTATUS status; + + +#if HOOK_NETWORK + if (TDIDispatch(pDeviceObject, pIrp, &status) == TRUE) + { + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_VERBOSE, ("DriverCleanup(%x, %x): TDIDispatch\n", pDeviceObject, pIrp)); + return status; + } +#endif + + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverCleanup(%x, %x)\n", pDeviceObject, pIrp)); + + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = 0; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + + +#if 0 +NTSTATUS +DriverRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp) +{ + PDEVICE_EXTENSION pDeviceExtension; + PIO_STACK_LOCATION pIrpStack; + ULONG size = 0; + + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("DriverRead()\n")); + + pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + + pDeviceExtension = (PDEVICE_EXTENSION) pDeviceObject->DeviceExtension; +/* + size = min(pDeviceExtension->BufferSize, pIrpStack->Parameters.Read.Length); + + RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer, pDeviceExtension->Buffer, size); + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("Wrote %d bytes: %s\n", size, pDeviceExtension->Buffer)); + + pDeviceExtension->BufferSize = 0; +*/ + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = size; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + + +NTSTATUS +DriverWrite(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp) +{ + PDEVICE_EXTENSION pDeviceExtension; + PIO_STACK_LOCATION pIrpStack; + ULONG size = 0; + + + LOG(LOG_SS_DRIVER_INTERNAL,LOG_PRIORITY_DEBUG, ("DriverWrite()\n")); + + pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + + pDeviceExtension = (PDEVICE_EXTENSION) pDeviceObject->DeviceExtension; +/* + size = min(128, pIrpStack->Parameters.Write.Length); + RtlCopyMemory(pDeviceExtension->Buffer, pIrp->AssociatedIrp.SystemBuffer, size); + + pDeviceExtension->BufferSize = size; + + LOG(LOG_SS_DRIVER_INTERNAL, LOG_PRIORITY_DEBUG, ("Read %d bytes: %s\n", size, pDeviceExtension->Buffer)); +*/ + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = size; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} +#endif diff --git a/driver.h b/driver.h new file mode 100644 index 0000000..e7f23fb --- /dev/null +++ b/driver.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * driver.h + * + * Abstract: + * + * This module defines various types used by the driver "plumbing" code. + * + * Author: + * + * Eugene Tsyrklevich 9-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __DRIVER_H__ +#define __DRIVER_H__ + + + +typedef struct _DEVICE_EXTENSION +{ + PDEVICE_OBJECT pDeviceObject; + UNICODE_STRING usSymLink; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + + +#define DEVICE_NAME L"\\Device\\Ozone" +#define DEVICE_SYMLINK_NAME L"\\??\\Ozone" + + +#define IOCTL_REGISTER_AGENT_SERVICE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_GET_ALERT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_GET_USERLAND_REQUEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_SEND_USERLAND_SID_RESOLVE_REPLY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_SEND_USERLAND_ASK_USER_REPLY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_START_CREATE_POLICY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_STOP_CREATE_POLICY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS) + + +// Build 23 +#define DRIVER_VERSION 0x00000023 + + +extern BOOLEAN ActiveUserAgent; + + +VOID DriverUnload(IN PDRIVER_OBJECT pDriverObject); +NTSTATUS DriverCreate(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp); +NTSTATUS DriverCleanup(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp); +NTSTATUS DriverClose (IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp); +NTSTATUS DriverRead (IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp); +NTSTATUS DriverWrite (IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp); +NTSTATUS DriverDeviceControl(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp); + + +/* + HRSRC hRsrc; + HGLOBAL hDriverResource; + DWORD dwDriverSize; + LPVOID lpvDriver; + HFILE hfTempFile; + • + • + • + + hRsrc = FindResource(hInst,MAKEINTRESOURCE(MSJDATNT),"BINRES"); + + hDriverResource = LoadResource(hInst, hRsrc); + dwDriverSize = SizeofResource(hInst, hRsrc); + lpvDriver = LockResource(hDriverResource); + + hfTempFile = _lcreat("msj.tmp",0); + _hwrite(hfTempFile, lpvDriver, dwDriverSize); + _lclose(hfTempFile); + + + http://www.microsoft.com/MSJ/0398/DRIVER.aspx + + */ + + +#endif /* __DRIVER_H__ */ \ No newline at end of file diff --git a/driver.sln b/driver.sln new file mode 100644 index 0000000..a092581 --- /dev/null +++ b/driver.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "driver", "driver.vcproj", "{D5256063-DBCD-44E3-890B-682CBCF94848}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {D5256063-DBCD-44E3-890B-682CBCF94848}.Debug.ActiveCfg = Debug|Win32 + {D5256063-DBCD-44E3-890B-682CBCF94848}.Debug.Build.0 = Debug|Win32 + {D5256063-DBCD-44E3-890B-682CBCF94848}.Release.ActiveCfg = Release|Win32 + {D5256063-DBCD-44E3-890B-682CBCF94848}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/driver.vcproj b/driver.vcproj new file mode 100644 index 0000000..806bc25 --- /dev/null +++ b/driver.vcproj @@ -0,0 +1,341 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/driverobj.c b/driverobj.c new file mode 100644 index 0000000..9b159d4 --- /dev/null +++ b/driverobj.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * driverobj.c + * + * Abstract: + * + * This module implements various driver object hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 06-Apr-2004 + * + * Revision History: + * + * None. + */ + + +#include "driverobj.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitDriverObjectHooks) +#endif + + +fpZwLoadDriver OriginalNtLoadDriver = NULL; +fpZwUnloadDriver OriginalNtUnloadDriver = NULL; + + +/* + * HookedNtLoadDriver() + * + * Description: + * This function mediates the NtLoadDriver() system service and checks the + * provided driver object name against the global and current process security policies. + * + * NOTE: ZwLoadDriver loads a device driver. [NAR] + * + * Parameters: + * Those of NtLoadDriver(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtLoadDriver(). + */ + +NTSTATUS +NTAPI +HookedNtLoadDriver +( + IN PUNICODE_STRING DriverServiceName +) +{ + PCHAR FunctionName = "HookedNtLoadDriver"; + UNICODE_STRING usDriverName; + ANSI_STRING AnsiDriverName; + CHAR DRIVERNAME[MAX_PATH]; + + + HOOK_ROUTINE_ENTER(); + + + if (!VerifyUnicodeString(DriverServiceName, &usDriverName)) + { + LOG(LOG_SS_DRIVER, LOG_PRIORITY_DEBUG, ("HookedNtLoadDriver: VerifyUnicodeString(%x) failed\n", DriverServiceName)); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + if (_snprintf(DRIVERNAME, MAX_PATH, "%S", usDriverName.Buffer) < 0) + { + LOG(LOG_SS_DRIVER, LOG_PRIORITY_DEBUG, ("%s: Driver name '%S' is too long\n", FunctionName, usDriverName.Buffer)); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + LOG(LOG_SS_DRIVER, LOG_PRIORITY_VERBOSE, ("HookedNtLoadDriver: %s\n", DRIVERNAME)); + + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(DRIVER, OP_REGLOAD); + } + + + ASSERT(OriginalNtLoadDriver); + + rc = OriginalNtLoadDriver(DriverServiceName); + + + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(DRIVER, DRIVERNAME, OP_REGLOAD); +} + + + +/* + * HookedNtUnloadDriver() + * + * Description: + * This function mediates the NtUnloadDriver() system service and checks the + * provided driver object name against the global and current process security policies. + * + * NOTE: ZwUnloadDriver unloads a device driver. [NAR] + * + * Parameters: + * Those of NtUnloadDriver(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtUnloadDriver(). + */ + +//XXX cannot mediate this function if we want to be able to unload our own driver +/* uncomment originalfunction pointer code in Init() routine and hookproc.c hook to enable +NTSTATUS +NTAPI +HookedNtUnloadDriver +( + IN PUNICODE_STRING DriverServiceName +) +{ + PCHAR FunctionName = "HookedNtUnloadDriver"; + UNICODE_STRING usDriverName; + ANSI_STRING AnsiDriverName; + CHAR DRIVERNAME[MAX_PATH]; + + + HOOK_ROUTINE_ENTER(); + + + if (!VerifyUnicodeString(DriverServiceName, &usDriverName)) + { + LOG(LOG_SS_DRIVER, LOG_PRIORITY_DEBUG, ("HookedNtUnloadDriver: VerifyUnicodeString failed\n")); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + AnsiDriverName.Length = 0; + AnsiDriverName.MaximumLength = MAX_PATH - 1; + AnsiDriverName.Buffer = DRIVERNAME; + + if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiDriverName, &usDriverName, FALSE))) + { + LOG(LOG_SS_DRIVER, LOG_PRIORITY_DEBUG, ("HookedNtUnloadDriver: RtlUnicodeStringToAnsiString failed\n")); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + DRIVERNAME[AnsiDriverName.Length] = 0; + + + LOG(LOG_SS_DRIVER, LOG_PRIORITY_DEBUG, ("HookedNtUnloadDriver: %s\n", DRIVERNAME)); + + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(DRIVER, OP_UNLOAD); + } + + + ASSERT(OriginalNtUnloadDriver); + + rc = OriginalNtUnloadDriver(DriverServiceName); + + + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(DRIVER, DRIVERNAME, OP_UNLOAD); +} +*/ + + +/* + * InitDriverHooks() + * + * Description: + * Initializes all the mediated driver object operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitDriverObjectHooks() +{ + if ( (OriginalNtLoadDriver = (fpZwLoadDriver) ZwCalls[ZW_LOAD_DRIVER_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_DRIVER, LOG_PRIORITY_DEBUG, ("InitDriverHooks: OriginalNtLoadDriver is NULL\n")); + return FALSE; + } +/* + if ( (OriginalNtUnloadDriver = (fpZwUnloadDriver) ZwCalls[ZW_UNLOAD_DRIVER_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_DRIVER, LOG_PRIORITY_DEBUG, ("InitDriverHooks: OriginalNtUnloadDriver is NULL\n")); + return FALSE; + } +*/ + return TRUE; +} diff --git a/driverobj.h b/driverobj.h new file mode 100644 index 0000000..ea7d276 --- /dev/null +++ b/driverobj.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * driverobj.h + * + * Abstract: + * + * This module defines various types used by driver object hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 06-Apr-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __DRIVEROBJ_H__ +#define __DRIVEROBJ_H__ + + +#include +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" +#include "log.h" + + +/* + * ZwLoadDriver loads a device driver. [NAR] + */ + +typedef NTSTATUS (*fpZwLoadDriver) ( + IN PUNICODE_STRING DriverServiceName + ); + +NTSTATUS +NTAPI +HookedNtLoadDriver( + IN PUNICODE_STRING DriverServiceName + ); + + +/* + * ZwUnloadDriver unloads a device driver. [NAR] + */ + +typedef NTSTATUS (*fpZwUnloadDriver) ( + IN PUNICODE_STRING DriverServiceName + ); + +NTSTATUS +NTAPI +HookedNtUnloadDriver( + IN PUNICODE_STRING DriverServiceName + ); + + +BOOLEAN InitDriverObjectHooks(); + + +#endif /* __DRIVEROBJ_H__ */ diff --git a/event.c b/event.c new file mode 100644 index 0000000..23fa528 --- /dev/null +++ b/event.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * event.c + * + * Abstract: + * + * This module implements various event hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 09-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "event.h" +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitEventHooks) +#endif + + +fpZwCreateEventPair OriginalNtCreateEventPair = NULL; +fpZwOpenEventPair OriginalNtOpenEventPair = NULL; + +fpZwCreateEvent OriginalNtCreateEvent = NULL; +fpZwOpenEvent OriginalNtOpenEvent = NULL; + + +/* + * HookedNtCreateEvent() + * + * Description: + * This function mediates the NtCreateEvent() system service and checks the + * provided event name against the global and current process security policies. + * + * NOTE: ZwCreateEvent creates or opens an event object. [NAR] + * + * Parameters: + * Those of NtCreateEvent(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateEvent(). + */ + +NTSTATUS +NTAPI +HookedNtCreateEvent +( + OUT PHANDLE EventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN EVENT_TYPE EventType, + IN BOOLEAN InitialState +) +{ + PCHAR FunctionName = "HookedNtCreateEvent"; + + + HOOK_ROUTINE_START(EVENT); + + + ASSERT(OriginalNtCreateEvent); + + rc = OriginalNtCreateEvent(EventHandle, DesiredAccess, ObjectAttributes, EventType, InitialState); + + + HOOK_ROUTINE_FINISH(EVENT); +} + + + +/* + * HookedNtOpenEvent() + * + * Description: + * This function mediates the NtOpenEvent() system service and checks the + * provided event name against the global and current process security policies. + * + * NOTE: ZwOpenEvent opens an event object. [NAR] + * + * Parameters: + * Those of NtOpenEvent(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtOpenEvent(). + */ + +NTSTATUS +NTAPI +HookedNtOpenEvent +( + OUT PHANDLE EventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtOpenEvent"; + + + HOOK_ROUTINE_START(EVENT); + + + ASSERT(OriginalNtOpenEvent); + + rc = OriginalNtOpenEvent(EventHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(EVENT); +} + + + +/* + * HookedNtCreateEventPair() + * + * Description: + * This function mediates the NtCreateEventPair() system service and checks the + * provided eventpair name against the global and current process security policies. + * + * NOTE: ZwCreateEventPair creates or opens an event pair object. [NAR] + * + * Parameters: + * Those of NtCreateEventPair(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateEventPair(). + */ + +NTSTATUS +NTAPI +HookedNtCreateEventPair +( + OUT PHANDLE EventPairHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtCreateEventPair"; + + + HOOK_ROUTINE_START(EVENT); + + + ASSERT(OriginalNtCreateEventPair); + + rc = OriginalNtCreateEventPair(EventPairHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(EVENT); +} + + + + +/* + * HookedNtOpenEventPair() + * + * Description: + * This function mediates the NtOpenEventPair() system service and checks the + * provided event name against the global and current process security policies. + * + * NOTE: ZwOpenEventPair opens an event pair object. [NAR] + * + * Parameters: + * Those of NtOpenEventPair(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtOpenEventPair(). + */ + +NTSTATUS +NTAPI +HookedNtOpenEventPair +( + OUT PHANDLE EventPairHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtOpenEventPair"; + + + HOOK_ROUTINE_START(EVENT); + + + ASSERT(OriginalNtOpenEventPair); + + rc = OriginalNtOpenEventPair(EventPairHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(EVENT); +} + + + +/* + * InitEventHooks() + * + * Description: + * Initializes all the mediated event operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitEventHooks() +{ + if ( (OriginalNtCreateEventPair = (fpZwCreateEventPair) ZwCalls[ZW_CREATE_EVENT_PAIR_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_EVENT, LOG_PRIORITY_DEBUG, ("InitEventHooks: OriginalNtCreateEventPair is NULL\n")); + return FALSE; + } + + if ( (OriginalNtOpenEventPair = (fpZwOpenEventPair) ZwCalls[ZW_OPEN_EVENT_PAIR_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_EVENT, LOG_PRIORITY_DEBUG, ("InitEventHooks: OriginalNtOpenEventPair is NULL\n")); + return FALSE; + } + + if ( (OriginalNtCreateEvent = (fpZwCreateEvent) ZwCalls[ZW_CREATE_EVENT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_EVENT, LOG_PRIORITY_DEBUG, ("InitEventHooks: OriginalNtCreateEvent is NULL\n")); + return FALSE; + } + + if ( (OriginalNtOpenEvent = (fpZwOpenEvent) ZwCalls[ZW_OPEN_EVENT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_EVENT, LOG_PRIORITY_DEBUG, ("InitEventHooks: OriginalNtOpenEvent is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/event.h b/event.h new file mode 100644 index 0000000..db0fc40 --- /dev/null +++ b/event.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * event.h + * + * Abstract: + * + * This module defines various types used by event hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 09-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __EVENT_H__ +#define __EVENT_H__ + + + +/* + * ZwCreateEvent creates or opens an event object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateEvent) ( + OUT PHANDLE EventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN EVENT_TYPE EventType, + IN BOOLEAN InitialState + ); + +NTSTATUS HookedNtCreateEvent( + OUT PHANDLE EventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN EVENT_TYPE EventType, + IN BOOLEAN InitialState + ); + + +/* + * ZwOpenEvent opens an event object. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenEvent) ( + OUT PHANDLE EventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS HookedNtOpenEvent( + OUT PHANDLE EventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +/* + * ZwCreateEventPair creates or opens an event pair object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateEventPair) ( + OUT PHANDLE EventPairHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS HookedNtCreateEventPair( + OUT PHANDLE EventPairHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +/* + * ZwOpenEventPair opens an event pair object. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenEventPair) ( + OUT PHANDLE EventPairHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS +NTAPI +HookedNtOpenEventPair( + OUT PHANDLE EventPairHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + + +BOOLEAN InitEventHooks(); + + +#endif /* __EVENT_H__ */ diff --git a/file.c b/file.c new file mode 100644 index 0000000..b82ed5c --- /dev/null +++ b/file.c @@ -0,0 +1,665 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * file.c + * + * Abstract: + * + * This module implements various file hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 19-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "file.h" +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitFileHooks) +#endif + + +fpZwCreateFile OriginalNtCreateFile = NULL; +fpZwOpenFile OriginalNtOpenFile = NULL; +fpZwDeleteFile OriginalNtDeleteFile = NULL; +fpZwQueryAttributesFile OriginalNtQueryAttributesFile = NULL; +fpZwQueryFullAttributesFile OriginalNtQueryFullAttributesFile = NULL; +fpZwQueryDirectoryFile OriginalNtQueryDirectoryFile = NULL; +fpZwSetInformationFile OriginalNtSetInformationFile = NULL; + +fpZwCreateMailslotFile OriginalNtCreateMailslotFile = NULL; +fpZwCreateNamedPipeFile OriginalNtCreateNamedPipeFile = NULL; + + + +// XXX make sure that this still works with POSIX subsystem (inside windows 2000 describes how to start posix subsystem) + +// XXX make sure streams don't screw anything up... do a search on a directory, observe NtCreateFile output.. + + +/* + * HookedNtCreateFile() + * + * Description: + * This function mediates the NtCreateFile() system service and checks the + * provided file name against the global and current process security policies. + * + * NOTE: ZwCreateFile() creates or opens a file. [NAR] + * + * Parameters: + * Those of NtCreateFile(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateFile(). + */ + +NTSTATUS +NTAPI +HookedNtCreateFile +( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PLARGE_INTEGER AllocationSize OPTIONAL, + IN ULONG FileAttributes, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + IN PVOID EaBuffer OPTIONAL, + IN ULONG EaLength +) +{ + PCHAR FunctionName = "HookedNtCreateFile"; + CHAR BufferLongName[MAX_PATH], BufferShortName[MAX_PATH]; + PCHAR FILENAME = BufferLongName;//BufferShortName; + PCHAR DIRECTORYNAME = BufferLongName;//BufferShortName; + BOOLEAN CreateDirectoryRequest = FALSE; + + + HOOK_ROUTINE_ENTER(); + + + /* special handling for directories, look at flags to figure out whether we are dealing w/a directory */ + if ((CreateOptions & FILE_DIRECTORY_FILE) && (CreateDisposition & FILE_CREATE)) + CreateDirectoryRequest = TRUE; + + + if (LearningMode == FALSE) + { + GetPathFromOA(ObjectAttributes, BufferLongName, MAX_PATH, RESOLVE_LINKS); + +// ConvertLongFileNameToShort(BufferLongName, BufferShortName, MAX_PATH); +//KdPrint(("%s\n%s\n", BufferLongName, BufferShortName)); + + if (CreateDirectoryRequest == TRUE) + { + POLICY_CHECK_OPTYPE(DIRECTORY, OP_DIR_CREATE); + } + else + { + POLICY_CHECK_OPTYPE(FILE, Get_FILE_OperationType(DesiredAccess)); + } + } + +//XXX if resolved name's first character is not '\' then allow? to allow names such as IDE#CdRomNECVMWar_VMware.. + + +/* +XXX +investigate + +The FileId can be used to open the file, when the FILE_OPEN_BY_FILE_ID +CreateOption is specified in a call to ZwCreateFile. + +whether this can be used to bypass name checking mechanism +*/ + if (CreateOptions & FILE_OPEN_BY_FILE_ID) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_WARNING, ("%d HookedNtCreateFile: FILE_OPEN_BY_FILE_ID set\n", (ULONG) PsGetCurrentProcessId())); + + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + ASSERT(OriginalNtCreateFile); + + rc = OriginalNtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, + AllocationSize, FileAttributes, ShareAccess, CreateDisposition, + CreateOptions, EaBuffer, EaLength); + + + if (CreateDirectoryRequest == TRUE) + { + HOOK_ROUTINE_FINISH_OPTYPE(DIRECTORY, OP_DIR_CREATE); + } + else + { + HOOK_ROUTINE_FINISH(FILE); + } +} + + + +/* + * HookedNtOpenFile() + * + * Description: + * This function mediates the NtOpenFile() system service and checks the + * provided file name against the global and current process security policies. + * + * NOTE: ZwOpenFile() opens a file. [NAR] + * + * Parameters: + * Those of NtOpenFile(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtOpenFile(). + */ + +NTSTATUS +NTAPI +HookedNtOpenFile +( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG ShareAccess, + IN ULONG OpenOptions +) +{ + PCHAR FunctionName = "HookedNtOpenFile"; +// HOOK_ROUTINE_START(FILE); + + CHAR BufferLongName[MAX_PATH], BufferShortName[MAX_PATH]; + PCHAR FILENAME = BufferLongName;//BufferShortName; + + + HOOK_ROUTINE_ENTER(); + + + if (LearningMode == FALSE) + { + GetPathFromOA(ObjectAttributes, BufferLongName, MAX_PATH, RESOLVE_LINKS); + +// ConvertLongFileNameToShort(BufferLongName, BufferShortName, MAX_PATH); +//KdPrint(("%s\n%s\n", BufferLongName, BufferShortName)); + + POLICY_CHECK_OPTYPE(FILE, Get_FILE_OperationType(DesiredAccess)); + } + + + ASSERT(OriginalNtOpenFile); + + rc = OriginalNtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, + ShareAccess, OpenOptions); + + + HOOK_ROUTINE_FINISH(FILE); +} + + + +/* + * HookedNtDeleteFile() + * + * Description: + * This function mediates the NtDeleteFile() system service and checks the + * provided file name against the global and current process security policies. + * + * NOTE: ZwDeleteFile deletes a file. [NAR] + * + * Parameters: + * Those of NtDeleteFile(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtDeleteFile(). + */ + +NTSTATUS +NTAPI +HookedNtDeleteFile +( + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtDeleteFile"; + + + HOOK_ROUTINE_START_OPTYPE(FILE, OP_DELETE); + + + ASSERT(OriginalNtDeleteFile); + + rc = OriginalNtDeleteFile(ObjectAttributes); + + + HOOK_ROUTINE_FINISH_OPTYPE(FILE, OP_DELETE); +} + + + +/* + * HookedNtQueryAttributesFile() + * + * Description: + * This function mediates the NtQueryAttributesFile() system service and checks the + * provided file name against the global and current process security policies. + * + * NOTE: ZwQueryAttributesFile retrieves basic information about a file object. [NAR] + * + * Parameters: + * Those of NtQueryAttributesFile(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtQueryAttributesFile(). + */ + +NTSTATUS +NTAPI +HookedNtQueryAttributesFile +( + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PFILE_BASIC_INFORMATION FileInformation +) +{ + PCHAR FunctionName = "HookedNtQueryAttributesFile"; + + + HOOK_ROUTINE_START_OPTYPE(FILE, OP_READ); + + + ASSERT(OriginalNtQueryAttributesFile); + + rc = OriginalNtQueryAttributesFile(ObjectAttributes, FileInformation); + + + HOOK_ROUTINE_FINISH_OPTYPE(FILE, OP_READ); +} + + + +/* + * HookedNtQueryFullAttributesFile() + * + * Description: + * This function mediates the NtQueryFullAttributesFile() system service and checks the + * provided file name against the global and current process security policies. + * + * NOTE: ZwQueryFullAttributesFile retrieves extended information about a file object. [NAR] + * + * Parameters: + * Those of NtQueryFullAttributesFile(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtQueryFullAttributesFile(). + */ + +NTSTATUS +NTAPI +HookedNtQueryFullAttributesFile +( + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PFILE_NETWORK_OPEN_INFORMATION FileInformation +) +{ + PCHAR FunctionName = "HookedNtQueryFullAttributesFile"; + + + HOOK_ROUTINE_START_OPTYPE(FILE, OP_READ); + + + ASSERT(OriginalNtQueryFullAttributesFile); + + rc = OriginalNtQueryFullAttributesFile(ObjectAttributes, FileInformation); + + + HOOK_ROUTINE_FINISH_OPTYPE(FILE, OP_READ); +} + + + +/* + * HookedNtQueryDirectoryFile() + * + * Description: + * This function mediates the NtQueryDirectoryFile() system service and checks the + * provided file name against the global and current process security policies. + * + * NOTE: ZwQueryDirectoryFile retrieves information about the contents of a directory. [NAR] + * + * Parameters: + * Those of NtQueryDirectoryFile(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtQueryDirectoryFile(). + */ + +NTSTATUS +NTAPI +HookedNtQueryDirectoryFile +( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID FileInformation, + IN ULONG FileInformationLength, + IN FILE_INFORMATION_CLASS FileInformationClass, + IN BOOLEAN ReturnSingleEntry, + IN PUNICODE_STRING FileName OPTIONAL, + IN BOOLEAN RestartScan +) +{ + PCHAR FunctionName = "HookedNtQueryDirectoryFile"; + UNICODE_STRING usInputFileName; + CHAR FILENAME[MAX_PATH]; + ANSI_STRING asFileName; + + + HOOK_ROUTINE_ENTER(); + + + if (ARGUMENT_PRESENT(FileName)) + { + if (!VerifyUnicodeString(FileName, &usInputFileName)) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("HookedNtQueryDirectoryFile: VerifyUnicodeString failed\n")); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + _snprintf(FILENAME, MAX_PATH, "%S", usInputFileName.Buffer); + FILENAME[ MAX_PATH - 1 ] = 0; + + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("HookedNtQueryDirectoryFile: %s\n", FILENAME)); + } + + + if (LearningMode == FALSE) + { + //XXX +// POLICY_CHECK_OPTYPE(FILE, OP_READ); + } + + + ASSERT(OriginalNtQueryDirectoryFile); + + rc = OriginalNtQueryDirectoryFile(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, + FileInformation, FileInformationLength, FileInformationClass, + ReturnSingleEntry, FileName, RestartScan); + + +// HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(FILE, FILENAME, OP_READ); + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * HookedNtSetInformationFile() + * + * Description: + * This function mediates the NtSetInformationFile() system service and checks the + * provided file name against the global and current process security policies. + * + * NOTE: ZwSetInformationFile sets information affecting a file object. [NAR] + * + * Parameters: + * Those of NtSetInformationFile(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtSetInformationFile(). + */ + +NTSTATUS +NTAPI +HookedNtSetInformationFile +( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PVOID FileInformation, + IN ULONG FileInformationLength, + IN FILE_INFORMATION_CLASS FileInformationClass +) +{ + PCHAR FunctionName = "HookedNtSetInformationFile"; + CHAR FILENAME[MAX_PATH]; + WCHAR FILENAMEW[MAX_PATH]; + PWSTR FileName = NULL; + UCHAR Operation = OP_READ; + + + HOOK_ROUTINE_ENTER(); + + + /* FileDispositionInformation is used to delete files */ + if (FileInformationClass == FileDispositionInformation) + Operation = OP_DELETE; + + + if ((FileName = GetNameFromHandle(FileHandle, FILENAMEW, sizeof(FILENAMEW))) != NULL) + { + sprintf(FILENAME, "%S", FileName); + + LOG(LOG_SS_FILE, LOG_PRIORITY_VERBOSE, ("%d %s: %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, FILENAME)); + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(FILE, Operation); + } + } + + + ASSERT(OriginalNtSetInformationFile); + + rc = OriginalNtSetInformationFile(FileHandle, IoStatusBlock, FileInformation, FileInformationLength, FileInformationClass); + + + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(FILE, FileName, Operation); +} + + + +/* + * HookedNtCreateNamedPipeFile() + * + * Description: + * This function mediates the NtCreateNamedPipeFile() system service and checks the + * provided named pipe name against the global and current process security policies. + * + * NOTE: ZwCreateNamedPipeFile creates a named pipe. [NAR] + * + * Parameters: + * Those of NtCreateNamedPipeFile(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateNamedPipeFile(). + */ + +NTSTATUS +NTAPI +HookedNtCreateNamedPipeFile +( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + IN ULONG TypeMessage, + IN ULONG ReadmodeMessage, + IN ULONG Nonblocking, + IN ULONG MaxInstances, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PLARGE_INTEGER DefaultTimeout OPTIONAL +) +{ + PCHAR FunctionName = "HookedNtCreateNamedPipeFile"; + + + HOOK_ROUTINE_START(NAMEDPIPE); + + + ASSERT(OriginalNtCreateNamedPipeFile); + + rc = OriginalNtCreateNamedPipeFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, + ShareAccess, CreateDisposition, CreateOptions, TypeMessage, + ReadmodeMessage, Nonblocking, MaxInstances, InBufferSize, + OutBufferSize, DefaultTimeout); + + + HOOK_ROUTINE_FINISH(NAMEDPIPE); +} + + + +/* + * HookedNtCreateMailslotFile() + * + * Description: + * This function mediates the NtCreateMailslotFile() system service and checks the + * provided mailslot name against the global and current process security policies. + * + * NOTE: ZwCreateMailslotFile creates a mailslot. [NAR] + * + * Parameters: + * Those of NtCreateMailslotFile(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateMailslotFile(). + */ + +NTSTATUS +NTAPI +HookedNtCreateMailslotFile +( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG CreateOptions, + IN ULONG InBufferSize, + IN ULONG MaxMessageSize, + IN PLARGE_INTEGER ReadTimeout OPTIONAL +) +{ + PCHAR FunctionName = "HookedNtCreateMailslotFile"; + + + HOOK_ROUTINE_START(MAILSLOT); + + + ASSERT(OriginalNtCreateMailslotFile); + + rc = OriginalNtCreateMailslotFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, + CreateOptions, InBufferSize, MaxMessageSize, ReadTimeout); + + + HOOK_ROUTINE_FINISH(MAILSLOT); +} + + + +/* + * InitFileHooks() + * + * Description: + * Initializes all the mediated file operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitFileHooks() +{ + if ( (OriginalNtCreateFile = (fpZwCreateFile) ZwCalls[ZW_CREATE_FILE_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("InitFileHooks: OriginalNtCreateFile is NULL\n")); + return FALSE; + } + + if ( (OriginalNtOpenFile = (fpZwOpenFile) ZwCalls[ZW_OPEN_FILE_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("InitFileHooks: OriginalNtOpenFile is NULL\n")); + return FALSE; + } + + if ( (OriginalNtDeleteFile = (fpZwDeleteFile) ZwCalls[ZW_DELETE_FILE_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("InitFileHooks: OriginalNtDeleteFile is NULL\n")); + return FALSE; + } + + if ( (OriginalNtQueryAttributesFile = (fpZwQueryAttributesFile) ZwCalls[ZW_QUERY_ATTRIBUTES_FILE_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("InitFileHooks: OriginalNtQueryAttributesFile is NULL\n")); + return FALSE; + } + + if ( (OriginalNtQueryFullAttributesFile = (fpZwQueryFullAttributesFile) ZwCalls[ZW_QUERY_FULLATTR_FILE_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("InitFileHooks: OriginalNtQueryFullAttributesFile is NULL\n")); + return FALSE; + } +/* + if ( (OriginalNtQueryDirectoryFile = (fpZwQueryDirectoryFile) ZwCalls[ZW_QUERY_DIRECTORYFILE_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("InitFileHooks: OriginalNtQueryDirectoryFile is NULL\n")); + return FALSE; + } +*/ + if ( (OriginalNtSetInformationFile = (fpZwSetInformationFile) ZwCalls[ZW_SET_INFO_FILE_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("InitFileHooks: OriginalNtSetInformationFile is NULL\n")); + return FALSE; + } + + if ( (OriginalNtCreateNamedPipeFile = (fpZwCreateNamedPipeFile) ZwCalls[ZW_CREATE_NAMEDPIPEFILE_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("InitFileHooks: OriginalNtCreateNamedPipeFile is NULL\n")); + return FALSE; + } + + if ( (OriginalNtCreateMailslotFile = (fpZwCreateMailslotFile) ZwCalls[ZW_CREATE_MAILSLOTFILE_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_FILE, LOG_PRIORITY_DEBUG, ("InitFileHooks: OriginalNtCreateMailslotFile is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/file.h b/file.h new file mode 100644 index 0000000..792a707 --- /dev/null +++ b/file.h @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * file.h + * + * Abstract: + * + * This module defines various types used by file hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 19-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __FILE_H__ +#define __FILE_H__ + + +/* + * ZwCreateFile creates or opens a file. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateFile) ( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PLARGE_INTEGER AllocationSize OPTIONAL, + IN ULONG FileAttributes, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + IN PVOID EaBuffer OPTIONAL, + IN ULONG EaLength + ); + +NTSTATUS +NTAPI +HookedNtCreateFile( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PLARGE_INTEGER AllocationSize OPTIONAL, + IN ULONG FileAttributes, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + IN PVOID EaBuffer OPTIONAL, + IN ULONG EaLength + ); + + +/* + * ZwOpenFile opens a file. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenFile) ( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG ShareAccess, + IN ULONG OpenOptions + ); + +NTSTATUS +NTAPI +HookedNtOpenFile( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG ShareAccess, + IN ULONG OpenOptions + ); + + +/* + * ZwDeleteFile deletes a file. [NAR] + */ + +typedef NTSTATUS (*fpZwDeleteFile) ( + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS +NTAPI +HookedNtDeleteFile( + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +/* + * ZwQueryDirectoryFile retrieves information about the contents of a directory. [NAR] + */ + +typedef NTSTATUS (*fpZwQueryDirectoryFile) ( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID FileInformation, + IN ULONG FileInformationLength, + IN FILE_INFORMATION_CLASS FileInformationClass, + IN BOOLEAN ReturnSingleEntry, + IN PUNICODE_STRING FileName OPTIONAL, + IN BOOLEAN RestartScan + ); + +NTSTATUS +NTAPI +HookedNtQueryDirectoryFile( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID FileInformation, + IN ULONG FileInformationLength, + IN FILE_INFORMATION_CLASS FileInformationClass, + IN BOOLEAN ReturnSingleEntry, + IN PUNICODE_STRING FileName OPTIONAL, + IN BOOLEAN RestartScan + ); + + +/* + * ZwQueryAttributesFile retrieves basic information about a file object. [NAR] + */ + +typedef NTSTATUS (*fpZwQueryAttributesFile) ( + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PFILE_BASIC_INFORMATION FileInformation + ); + +NTSTATUS +NTAPI +HookedNtQueryAttributesFile( + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PFILE_BASIC_INFORMATION FileInformation + ); + + +/* + * ZwQueryFullAttributesFile retrieves extended information about a file object. [NAR] + */ + +typedef NTSTATUS (*fpZwQueryFullAttributesFile) ( + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PFILE_NETWORK_OPEN_INFORMATION FileInformation + ); + +NTSTATUS +NTAPI +HookedNtQueryFullAttributesFile( + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PFILE_NETWORK_OPEN_INFORMATION FileInformation + ); + + +/* + * ZwSetInformationFile sets information affecting a file object. [NAR] + */ + +typedef NTSTATUS (*fpZwSetInformationFile) ( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PVOID FileInformation, + IN ULONG FileInformationLength, + IN FILE_INFORMATION_CLASS FileInformationClass + ); + +NTSTATUS +NTAPI +HookedNtSetInformationFile( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PVOID FileInformation, + IN ULONG FileInformationLength, + IN FILE_INFORMATION_CLASS FileInformationClass + ); + + + +/* + * ZwCreateNamedPipeFile creates a named pipe. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateNamedPipeFile) ( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, +/* The following 3 parameters listed in NAR are wrong + IN BOOLEAN TypeMessage, + IN BOOLEAN ReadmodeMessage, + IN BOOLEAN Nonblocking, +*/ + IN ULONG TypeMessage, + IN ULONG ReadmodeMessage, + IN ULONG Nonblocking, + IN ULONG MaxInstances, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PLARGE_INTEGER DefaultTimeout OPTIONAL + ); + +NTSTATUS +NTAPI +HookedNtCreateNamedPipeFile( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions, + IN ULONG TypeMessage, + IN ULONG ReadmodeMessage, + IN ULONG Nonblocking, + IN ULONG MaxInstances, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PLARGE_INTEGER DefaultTimeout OPTIONAL + ); + + + +/* + * ZwCreateMailslotFile creates a mailslot. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateMailslotFile) ( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG CreateOptions, + IN ULONG InBufferSize, + IN ULONG MaxMessageSize, + IN PLARGE_INTEGER ReadTimeout OPTIONAL + ); + +NTSTATUS +NTAPI +HookedNtCreateMailslotFile( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG CreateOptions, + IN ULONG InBufferSize, + IN ULONG MaxMessageSize, + IN PLARGE_INTEGER ReadTimeout OPTIONAL + ); + + +BOOLEAN InitFileHooks(); + + +#endif /* __FILE_H__ */ diff --git a/hookproc.c b/hookproc.c new file mode 100644 index 0000000..cba59d5 --- /dev/null +++ b/hookproc.c @@ -0,0 +1,1799 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * hookproc.c + * + * Abstract: + * + * This module implements various service operation (system call) hooking routines. + * + * Since not all the Zw* services are exported, we use a hack to find the required information. + * ntdll.dll contains stubs for calling all the system services. All stubs start with + * "mov eax, function_index" instruction where function_index is an index into a global + * system call table. By parsing ntdll.dll and extracting all the function indeces + * we can map all the Zw* names to their appropriate system call table indeces. + * + * Author: + * + * Eugene Tsyrklevich 16-Feb-2004 + * + * Revision History: + * + * None. + */ + +#include +#include "hookproc.h" +#include "sysinfo.h" +#include "ntproto.h" +#include "learn.h" +#include "file.h" +#include "registry.h" +#include "section.h" +#include "sysinfo.h" +#include "semaphore.h" +#include "dirobj.h" +#include "symlink.h" +#include "mutant.h" +#include "event.h" +#include "port.h" +#include "job.h" +#include "token.h" +#include "timer.h" +#include "driverobj.h" +#include "process.h" +#include "procname.h" +#include "time.h" +#include "atom.h" +#include "vdm.h" +#include "debug.h" +#include "i386.h" +#include "misc.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitSyscallsHooks) +#pragma alloc_text (INIT, InstallSyscallsHooks) +#pragma alloc_text (PAGE, RemoveSyscallsHooks) +#endif + + +#if DBG +int HookedRoutineRunning = 0; +#endif + +PCHAR NTDLL_Base; + +int ZwCallsNumber = 0; + + + +/* + * FindFunctionOffset() + * + * Description: + * Finds a Zw* system call offset in a system service table. + * + * Implementation of all the Zw* system services (such as ZwOpenFile or ZwCreateProcess()) + * start with a "mov eax, function_offset" instruction. Function offset is extracted from + * the first instruction. + * + * Parameters: + * Function - Pointer to the function code. + * + * Returns: + * Integer function offset (-1 in case of a failure). + */ + +/* "MOV EAX,IMM32" opcode */ +#define OPCODE_MOV 0xB8 + +/* macro shortcut for bailing out of FindFunctionOffset in case of an error */ + +#define ABORT_FindFunctionOffset(msg) { \ + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("Error occured in FindFunctionOffset():")); \ + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, msg); \ + return -1; \ + } + +int +FindFunctionOffset(PULONG_PTR Function) +{ + PUCHAR Instruction; + ULONG num, ServiceIDNumber, ServiceTableIndex; + + + /* + * Make sure that the service code starts with a MOV EAX,IMM32 instruction: + * + * lkd> u ZwCreateFile + * nt!ZwCreateFile: + * 804f86f4 b825000000 mov eax,0x25 + */ + + Instruction = (PUCHAR) Function; + + if (*Instruction != OPCODE_MOV) + + ABORT_FindFunctionOffset(("Invalid opcode %x\n", *Instruction)); + + + /* + * Extract the Service Descriptor Table index (4 bytes following the mov opcode) + * + * The index format is as follows: + * + * Leading 18 bits are all zeroes + * Following 2 bits are system service table index (3 bits on Win64) + * Following 12 bits are service number + */ + + num = * (PULONG) ++Instruction; + + + /* only SERVICE_TABLE_INDEX_BITS LSB bits should be set */ + + ServiceTableIndex = num >> SERVICE_ID_NUMBER_BITS; + + if (ServiceTableIndex >= NUMBER_SERVICE_TABLES) + + ABORT_FindFunctionOffset(("Invalid SSDT index: %x (%x)\n", ServiceTableIndex, num)); + + + /* XXX temporary? There exist 4 (8 on IA64) service tables. All the Zw* system services are in table 0 */ + if (ServiceTableIndex != 0) + + ABORT_FindFunctionOffset(("Invalid SSDT index2: %x (%x)\n", ServiceTableIndex, num)); + + + /* Verify Service ID Number is in range */ + + ServiceIDNumber = num & SERVICE_ID_NUMBER_MASK; + +//XXX shouldn't we be using the shadow table instead?? +//shadow table Zw* base address is the same in addition to GUI table + if (ServiceIDNumber > KeServiceDescriptorTable[ServiceTableIndex].NumberOfServices) + + ABORT_FindFunctionOffset(("Invalid service id number %d (max is %d)\n", ServiceIDNumber, KeServiceDescriptorTable[ServiceTableIndex].NumberOfServices)); + + + return ServiceIDNumber; +} + + + +#if 0 + +/* + * HookSystemService() + * + * Description: + * Replaces an existing sytem service pointer (n a global system service table) with another function pointer. + * + * Parameters: + * OldService - Pointer to the service code to mediate. + * NewService - Pointer to the new function code. + * + * Returns: + * Current OldService indexed system service function pointer. + */ + +/* macro shortcut for bailing out of HookSystemService in case of an error */ + +#define ABORT_HookSystemService(msg) { \ + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("Error occured in HookSystemService():")); \ + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, (msg)); \ + return NULL; \ + } + +PVOID +HookSystemService(PVOID OldService, PVOID NewService) +{ + PULONG_PTR ssdt; + PULONG_PTR retptr = NULL; + ULONG ServiceIDNumber; + + + if (OldService == NULL || NewService == NULL) + + ABORT_HookSystemService(("NULL detected. OldService=%x NewService=%x", OldService, NewService)); + + + ServiceIDNumber = FindFunctionOffset(OldService); + + if (ServiceIDNumber == -1) + + ABORT_HookSystemService(("FindFunctionOffset(%x) failed", OldService)); + + + ssdt = KeServiceDescriptorTable[0].ServiceTableBase; + + + retptr = (PULONG_PTR) ssdt[ServiceIDNumber]; + + if (retptr == NULL) + + ABORT_HookSystemService(("ssdt[index] = NULL\n")); + + + if (((ULONG) retptr & SystemAddressStart) == 0) + + ABORT_HookSystemService(("invalid code instruction specified\n")); + + + retptr = ExchangeReadOnlyMemoryPointer((PVOID *) &ssdt[ServiceIDNumber], NewService); + + + return retptr; +} + +#endif + + + +/* + * HookSystemServiceByIndex() + * + * Description: + * Replaces an existing sytem service (n a global system service table) with another function pointer. + * + * Parameters: + * ServiceIDNumber - Index of a system service to mediate. + * NewService - Pointer to the new function code. + * + * Returns: + * Current ServiceIDNumber indexed system service function pointer. + */ + +/* macro shortcut for bailing out of HookSystemServiceByIndex in case of an error */ + +#define ABORT_HookSystemServiceByIndex(msg) { \ + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("Error occured in HookSystemServiceByIndex():")); \ + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, msg); \ + return NULL; \ + } + +PVOID +HookSystemServiceByIndex(ULONG ServiceIDNumber, PVOID NewService) +{ + PULONG_PTR ssdt; + PULONG_PTR retptr = NULL; + ULONG ServiceTableIndex = 0; + + + ssdt = KeServiceDescriptorTable[ServiceTableIndex].ServiceTableBase; + + + /* Verify Service ID Number is in range */ + +//XXX shouldn't we be using the shadow table instead?? + if (ServiceIDNumber > KeServiceDescriptorTable[ServiceTableIndex].NumberOfServices) + + ABORT_HookSystemServiceByIndex(("Invalid service id number %d (max is %d)\n", ServiceIDNumber, KeServiceDescriptorTable[ServiceTableIndex].NumberOfServices)); + + + retptr = (PULONG_PTR) ssdt[ServiceIDNumber]; + + if (retptr == NULL) + + ABORT_HookSystemServiceByIndex(("ssdt[index] = NULL\n")); + + + if (((ULONG) retptr & SystemAddressStart) == 0) + + ABORT_HookSystemServiceByIndex(("invalid code instruction specified\n")); + + + retptr = ExchangeReadOnlyMemoryPointer((PVOID *) &ssdt[ServiceIDNumber], NewService); + + + return retptr; +} + + +#if 0 +/* + * FindSystemServiceByIndex() + * + * Description:XXX + * Replaces an existing sytem service (n a global system service table) with another function pointer. + * + * Parameters: + * ServiceIDNumber - Index of a system service to mediate. + * NewService - Pointer to the new function code. + * + * Returns: + * Current ServiceIDNumber indexed system service function pointer. + */ + +/* macro shortcut for bailing out of HookSystemServiceByIndex in case of an error */ + +#define ABORT_FindSystemServiceByIndex(msg) { LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("Error occured in FindSystemServiceByIndex():")); LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, msg); return NULL; } + +PULONG +FindSystemServiceByIndex(ULONG ServiceIDNumber) +{ + PULONG_PTR ssdt; + PULONG_PTR retptr = NULL; + ULONG ServiceTableIndex = 0; + + + ssdt = KeServiceDescriptorTable[ServiceTableIndex].ServiceTableBase; + + + /* Verify Service ID Number is in range */ + +//XXX shouldn't we be using the shadow table instead?? + if (ServiceIDNumber > KeServiceDescriptorTable[ServiceTableIndex].NumberOfServices) + + ABORT_FindSystemServiceByIndex(("Invalid service id number %d (max is %d)\n", ServiceIDNumber, KeServiceDescriptorTable[ServiceTableIndex].NumberOfServices)); + + + retptr = (PULONG_PTR) ssdt[ServiceIDNumber]; + + if (retptr == NULL) + + ABORT_FindSystemServiceByIndex(("ssdt[index] = NULL\n")); + + + if (((ULONG) retptr & SystemAddressStart) == 0) + + ABORT_FindSystemServiceByIndex(("invalid code instruction specified\n")); + + + return (PULONG) ssdt[ServiceIDNumber]; +} +#endif + + +/* + * HookSystemServiceByName() + * + * Description: + * Replaces an existing sytem service (n a global system service table) with another function pointer. + * + * Parameters: + * ServiceName - Name of a Zw* system service to mediate. + * HookFunction - Pointer to the mediator function code. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +HookSystemServiceByName(PCHAR ServiceName, PULONG_PTR HookFunction) +{ + int i; + + + for (i = 0; i < ZwCallsNumber; i++) + { + if (strlen(ServiceName) == ZwCalls[i].ZwNameLength && _stricmp(ServiceName, ZwCalls[i].ZwName + 2) == 0) + { +// LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("HookSystemServiceByName: Matched rule: %x\n", ZwCalls[i].ServiceIDNumber)); + + // hijack the syscall if not hijacked already + if (ZwCalls[i].Hijacked == FALSE && ZwCalls[i].ServiceIDNumber != -1) + { + if ((ZwCalls[i].OriginalFunction = HookSystemServiceByIndex(ZwCalls[i].ServiceIDNumber, + HookFunction ? HookFunction : ZwCalls[i].HookFunction)) != NULL) + { + ZwCalls[i].Hijacked = TRUE; + } + } + + return TRUE; + } + } + + + return FALSE; +} + + + +#if 0 +/* + * FindSystemServiceIndex() + * + * Description: + * Find a system service index for a specified service name. + * + * Parameters: + * ServiceName - Name of a Zw* system service to find. + * + * Returns: + * System service index if found, -1 otherwise. + */ + +ULONG +FindSystemServiceIndex(PCHAR ServiceName) +{ + int i; + + + for (i = 0; i < ZwCallsNumber; i++) + { + if (strlen(ServiceName) == ZwCalls[i].ZwNameLength && _stricmp(ServiceName, ZwCalls[i].ZwName + 2) == 0) + { +// LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("FindSystemServiceByName: Matched rule: %x\n", ZwCalls[i].ServiceIDNumber)); + + return ZwCalls[i].ServiceIDNumber; + } + } + + + return -1; +} +#endif + + +/* + * FindSystemServiceNumber() + * + * Description: + * Find a system service number for a specified service name. + * + * Parameters: + * ServiceName - Name of a Zw* system service to find. + * + * Returns: + * System service number if found, -1 otherwise. + */ + +ULONG +FindSystemServiceNumber(PCHAR ServiceName) +{ + int i; + + + for (i = 0; i < ZwCallsNumber; i++) + { + if (strlen(ServiceName) == ZwCalls[i].ZwNameLength && _stricmp(ServiceName, ZwCalls[i].ZwName + 2) == 0) + { +// LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("FindSystemServiceByName: Matched rule: %x\n", ZwCalls[i].ServiceIDNumber)); + + return i; + } + } + + + return -1; +} + + + +/* + * Find_NTDLL_Base() + * + * Description: + * Returns the base address of mapped ntdll.dll in a "System" process context. + * + * NOTE: Based on "Windows NT/2000 Native API Reference" implementation. + * + * Parameters: + * None. + * + * Returns: + * ntdll.dll base address (NULL if not found). + */ + +PVOID +Find_NTDLL_Base() +{ + ULONG size, i; + PVOID ntdll = NULL; + PULONG SystemInfo; + PSYSTEM_MODULE_INFORMATION pSMI; + NTSTATUS status; + + + /* + * The data returned to the SystemInformation buffer is a ULONG count of the number of + * modules followed immediately by an array of SYSTEM_MODULE_INFORMATION. + * + * The system modules are the Portable Executable (PE) format files loaded into the + * kernel address space (ntoskrnl.exe, hal.dll, device drivers, and so on) and ntdll.dll. + */ + + + /* first, find out the total amount of module information to be returned */ + + status = ZwQuerySystemInformation(SystemModuleInformation, &size, 0, &size); + if (size == 0 || size > 1024*1024) + { + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("Find_NTDLL_Base: ZwQuerySystemInformation failed. status=%x size=%d\n", status, size)); + return NULL; + } + + + /* second, allocate the required amount of memory */ + + SystemInfo = ExAllocatePoolWithTag(PagedPool, size + 4, _POOL_TAG); + if (SystemInfo == NULL) + { + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("Find_NTDLL_Base: out of memory (requested %d bytes)\n", size + 4)); + return NULL; + } + + + /* third, request the module information */ + + ZwQuerySystemInformation(SystemModuleInformation, SystemInfo, size, &i); + + if (size != ((*SystemInfo * sizeof(SYSTEM_MODULE_INFORMATION)) + 4)) + { + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("Find_NTDLL_Base: inconsistent size (%d != %d * %d + 4)\n", size, *SystemInfo, sizeof(SYSTEM_MODULE_INFORMATION))); + return NULL; + } + + + + pSMI = (PSYSTEM_MODULE_INFORMATION) (SystemInfo + 1); + + for (i = 0; i < *SystemInfo; i++) + { +// LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("comparing '%s' (base 0x%x)\n", pSMI[i].ImageName + pSMI[i].ModuleNameOffset, pSMI[i].Base)); + + if (_stricmp(pSMI[i].ImageName + pSMI[i].ModuleNameOffset, "ntdll.dll") == 0) + { + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_VERBOSE, ("Find_NTDLL_Base: ntdll.dll base address is %x\n", pSMI[i].Base)); + ntdll = pSMI[i].Base; + break; + } + } + + ExFreePoolWithTag(SystemInfo, _POOL_TAG); + + + return ntdll; +} + + +#if 0 +PVOID +Find_Kernel32_Base2() +{ + PVOID Kernel32 = NULL; + NTSTATUS status; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK isb; + HANDLE FileHandle; + UNICODE_STRING usName; + PVOID BaseAddress = NULL; + SIZE_T Size; + CHAR buffer[256]; + + +// RtlInitUnicodeString(&usName, L"\\SystemRoot\\System32\\kernel32.dll"); + RtlInitUnicodeString(&usName, L"\\??\\c:\\windows\\system32\\kernel32.dll"); + InitializeObjectAttributes(&ObjectAttributes, &usName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + status = ZwCreateFile(&FileHandle, GENERIC_READ, &ObjectAttributes, &isb, NULL, 0, 0, 0, 0, NULL, 0); + if (!NT_SUCCESS(status)) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("Find_Kernel32_Base: Failed to open file %S (%x)\n", usName.Buffer, status)); + return FALSE; + } + + + status = ZwReadFile(FileHandle, NULL, NULL, NULL, &isb, (PVOID) buffer, sizeof(buffer) - 1, 0, NULL); + + if (! NT_SUCCESS(status)) + { + if (status != STATUS_END_OF_FILE) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("LoadSecurityPolicy: ZwReadFile failed rc=%x\n", status)); + } + } + + ZwClose(FileHandle); + + + return Kernel32; +} + + + +PVOID +Find_Kernel32_Base() +{ + PVOID Kernel32 = NULL; + NTSTATUS status; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE SectionHandle; + UNICODE_STRING usName; + PVOID BaseAddress = NULL; + SIZE_T Size; + NTSTATUS q; + + + RtlInitUnicodeString(&usName, L"\\KnownDlls\\kernel32.dll"); + InitializeObjectAttributes(&ObjectAttributes, &usName, OBJ_KERNEL_HANDLE, NULL, NULL); + + if (NT_SUCCESS( ZwOpenSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes) )) + { + q = ZwMapViewOfSection(SectionHandle, NtCurrentProcess(), &BaseAddress, 0, 0, NULL, &Size, ViewShare, MEM_RESERVE, PAGE_READWRITE); + if (NT_SUCCESS(q)) + { + KdPrint(("XXX mapped kernel32.dll at BaseAddress %x\n", BaseAddress)); + + if (!NT_SUCCESS( ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress) )) + { + KdPrint(("ZwUnmapViewOfSection failed")); + } + + ZwClose(SectionHandle); + } + else + { + KdPrint(("ZwMapViewOfSection failed with status %x", q)); + } + } + else + { + KdPrint(("ZwOpenSection failed")); + } + + return Kernel32; +} +#endif + + +/* + * FindFunctionBase() + * + * Description: + * Finds the address where a function is mapped at. + * + * NOTE: Based on "Windows NT/2000 Native API Reference" implementation. + * + * Parameters: + * Base - Mapped address of a DLL exporting the function. + * Name - Function name. + * + * Returns: + * Address where a function is mapped at (NULL if not found). + */ + +/* macro shortcut for bailing out of FindFunctionBase in case of an error */ + +#define ABORT_FindFunctionBase(msg) { \ + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("Error occured in FindFunctionBase():")); \ + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, msg); \ + return NULL; \ + } + +PVOID +FindFunctionBase(PCHAR ImageBase, PCSTR Name) +{ + PIMAGE_DOS_HEADER DosHeader; + PIMAGE_NT_HEADERS PeHeader; + PIMAGE_DATA_DIRECTORY ImageExportDirectoryEntry; + ULONG ExportDirectorySize, ExportDirectoryOffset, i; + PIMAGE_EXPORT_DIRECTORY ExportDirectory; + PULONG ExportAddressTable; + PSHORT ExportOrdinalTable; + PULONG ExportNameTable; + + + if ( (DosHeader = (PIMAGE_DOS_HEADER) ImageBase) == NULL) + + ABORT_FindFunctionBase(("Base pointer is NULL (name = %s)\n", Name)); + + + if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) + + ABORT_FindFunctionBase(("DOS Signature not found! (%x %x)\n", DosHeader, DosHeader->e_magic)); + + + if (DosHeader->e_lfanew > 1024*1024) + + ABORT_FindFunctionBase(("Invalid e_lfanew value %x\n", DosHeader->e_lfanew)); + + + if ( (PeHeader = (PIMAGE_NT_HEADERS) (ImageBase + DosHeader->e_lfanew)) == NULL) + + ABORT_FindFunctionBase(("NT header pointer is NULL (name = %s)\n", Name)); + + + if (PeHeader->Signature != IMAGE_PE_SIGNATURE) + + ABORT_FindFunctionBase(("PE Signature not found! (%x %x)\n", PeHeader, PeHeader->Signature)); + + + if ( (ImageExportDirectoryEntry = PeHeader->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT) == NULL) + + ABORT_FindFunctionBase(("Export directory pointer is NULL (name = %s)\n", Name)); + + + ExportDirectorySize = ImageExportDirectoryEntry->Size; + ExportDirectoryOffset = ImageExportDirectoryEntry->VirtualAddress; + + if ( (ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) (ImageBase + ExportDirectoryOffset)) == NULL) + + ABORT_FindFunctionBase(("Exports pointer is NULL (name = %s)\n", Name)); + + + ExportAddressTable = (PULONG) (ImageBase + ExportDirectory->AddressOfFunctions); + ExportOrdinalTable = (PSHORT) (ImageBase + ExportDirectory->AddressOfNameOrdinals); + ExportNameTable = (PULONG) (ImageBase + ExportDirectory->AddressOfNames); + +//XXX "AcceptConnectPort\0" \0 necessary to make sure ZwCreateProcess does not match ZwCreateProcessEx ? + + for (i = 0; i < ExportDirectory->NumberOfNames; i++) + { + ULONG ord = ExportOrdinalTable[i]; + + if (ExportAddressTable[ord] < ExportDirectoryOffset || + ExportAddressTable[ord] >= ExportDirectoryOffset + ExportDirectorySize) + { + //XXX windows loader uses binary search? + if (strcmp(ImageBase + ExportNameTable[i], Name) == 0) + { + return ImageBase + ExportAddressTable[ord]; + } + } + } + + return NULL; +} + + + +/* + * FindZwFunctionIndex() + * + * Description: + * Finds a system service table index of a Zw* system service. + * + * NOTE: Based on "Windows NT/2000 Native API Reference" implementation. + * + * Parameters: + * Name - Zw* service name. + * + * Returns: + * Zw* service index in a system service table (-1 if not found). + */ + +int +FindZwFunctionIndex(PCSTR Name) +{ + PULONG_PTR FunctionBase; + + + if (NTDLL_Base == NULL) + if ( (NTDLL_Base = Find_NTDLL_Base()) == NULL) + return -1; + + + if ( (FunctionBase = FindFunctionBase(NTDLL_Base, Name)) == NULL) + { +// LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("FindZwFunctionIndex: FindZwFunctionBase(%s) returned NULL", Name)); + return -1; + } + + + return FindFunctionOffset(FunctionBase); +} + + + +/* + * ZwCalls structure describes all known system services. + * Part of the structure (i.e. system call table offset and system call address) + * is filled in at runtime. + * + * Automatically generated with + * dumpbin /exports c:\windows2003\system32\ntdll.dll|grep Zw| + * perl -wnle "print qq{\t{ \"$1\", NULL, (PULONG_PTR) SystemCallHandler$i, NULL, FALSE },} if /(Zw.*)/; ++$i" > win2k3_syscalls + * + * perl -wne "if (/\"Zw(.*?)\"/) { $q=length $1; s/\"Zw(.*?)\"/\"Zw$1\", $q/} print" q.txt > q2.txt + */ + +struct _ZwCalls /* { + PCHAR ZwName; // System call name + USHORT ZwNameLength; // System call name length + USHORT ServiceIDNumber; // System call index (filled in at runtime) + PULONG_PTR HookFunction; // Address of the hijacking function (function that will be called instead of the original system call) + PULONG_PTR OriginalFunction; // PlaceHolder for the address of the original syscall address + BOOLEAN Hijacked; // Flag indicating whether we already hijacked this system call + // or whether this is a special system service that needs to be hijacked initially +} */ ZwCalls[] = +{ + { "ZwAcceptConnectPort", 17, -1, (PULONG_PTR) SystemCallHandler0, NULL, FALSE }, + { "ZwAccessCheck", 11, -1, (PULONG_PTR) SystemCallHandler1, NULL, FALSE }, + { "ZwAccessCheckAndAuditAlarm", 24, -1, (PULONG_PTR) SystemCallHandler2, NULL, FALSE }, + { "ZwAccessCheckByType", 17, -1, (PULONG_PTR) SystemCallHandler3, NULL, FALSE }, + { "ZwAccessCheckByTypeAndAuditAlarm", 30, -1, (PULONG_PTR) SystemCallHandler4, NULL, FALSE }, + { "ZwAccessCheckByTypeResultList", 27, -1, (PULONG_PTR) SystemCallHandler5, NULL, FALSE }, + { "ZwAccessCheckByTypeResultListAndAuditAlarm", 40, -1, (PULONG_PTR) SystemCallHandler6, NULL, FALSE }, + { "ZwAccessCheckByTypeResultListAndAuditAlarmByHandle", 48, -1, (PULONG_PTR) SystemCallHandler7, NULL, FALSE }, + + +#if HOOK_ATOM + { "ZwAddAtom", 7, -1, (PULONG_PTR) HookedNtAddAtom, NULL, TRUE }, +#else + { "ZwAddAtom", 7, -1, (PULONG_PTR) SystemCallHandler8, NULL, FALSE }, +#endif + + +//XXX + { "ZwAddBootEntry", 12, -1, (PULONG_PTR) SystemCallHandler9, NULL, FALSE }, + { "ZwAddDriverEntry", 14, -1, (PULONG_PTR) SystemCallHandler10, NULL, FALSE }, + + +#if HOOK_TOKEN_ZZZ + { "ZwAdjustGroupsToken", 17, -1, (PULONG_PTR) HookedNtAdjustGroupsToken, NULL, TRUE }, +#else + { "ZwAdjustGroupsToken", 17, -1, (PULONG_PTR) SystemCallHandler11, NULL, FALSE }, +#endif + +#if HOOK_TOKEN + { "ZwAdjustPrivilegesToken", 21, -1, (PULONG_PTR) HookedNtAdjustPrivilegesToken, NULL, TRUE }, +#else + { "ZwAdjustPrivilegesToken", 21, -1, (PULONG_PTR) SystemCallHandler12, NULL, FALSE }, +#endif + + + { "ZwAlertResumeThread", 17, -1, (PULONG_PTR) SystemCallHandler13, NULL, FALSE }, + { "ZwAlertThread", 11, -1, (PULONG_PTR) SystemCallHandler14, NULL, FALSE }, + { "ZwAllocateLocallyUniqueId", 23, -1, (PULONG_PTR) SystemCallHandler15, NULL, FALSE }, + { "ZwAllocateUserPhysicalPages", 25, -1, (PULONG_PTR) SystemCallHandler16, NULL, FALSE }, + { "ZwAllocateUuids", 13, -1, (PULONG_PTR) SystemCallHandler17, NULL, FALSE }, + { "ZwAllocateVirtualMemory", 21, -1, (PULONG_PTR) SystemCallHandler18, NULL, FALSE }, + { "ZwApphelpCacheControl", 19, -1, (PULONG_PTR) SystemCallHandler19, NULL, FALSE }, + { "ZwAreMappedFilesTheSame", 21, -1, (PULONG_PTR) SystemCallHandler20, NULL, FALSE }, + { "ZwAssignProcessToJobObject", 24, -1, (PULONG_PTR) SystemCallHandler21, NULL, FALSE }, + { "ZwCallbackReturn", 14, -1, (PULONG_PTR) SystemCallHandler22, NULL, FALSE }, + { "ZwCancelDeviceWakeupRequest", 25, -1, (PULONG_PTR) SystemCallHandler23, NULL, FALSE }, + { "ZwCancelIoFile", 12, -1, (PULONG_PTR) SystemCallHandler24, NULL, FALSE }, + { "ZwCancelTimer", 11, -1, (PULONG_PTR) SystemCallHandler25, NULL, FALSE }, + { "ZwClearEvent", 10, -1, (PULONG_PTR) SystemCallHandler26, NULL, FALSE }, + + /* don't mediate for performance reasons, requires a valid handle anyway */ + { "ZwClose", 5, -1, NULL, NULL, FALSE }, + + { "ZwCloseObjectAuditAlarm", 21, -1, (PULONG_PTR) SystemCallHandler28, NULL, FALSE }, + { "ZwCompactKeys", 11, -1, (PULONG_PTR) SystemCallHandler29, NULL, FALSE }, + { "ZwCompareTokens", 13, -1, (PULONG_PTR) SystemCallHandler30, NULL, FALSE }, + { "ZwCompleteConnectPort", 19, -1, (PULONG_PTR) SystemCallHandler31, NULL, FALSE }, + { "ZwCompressKey", 11, -1, (PULONG_PTR) SystemCallHandler32, NULL, FALSE }, + + +#if HOOK_PORT + { "ZwConnectPort", 11, -1, (PULONG_PTR) HookedNtConnectPort, NULL, TRUE }, +#else + { "ZwConnectPort", 11, -1, (PULONG_PTR) SystemCallHandler33, NULL, FALSE }, +#endif + + + { "ZwContinue", 8, -1, (PULONG_PTR) SystemCallHandler34, NULL, FALSE }, + + +#if HOOK_DEBUG_ZZZ + { "ZwCreateDebugObject", 17, -1, (PULONG_PTR) HookedNtCreateDebugObject, NULL, TRUE }, +#else + { "ZwCreateDebugObject", 17, -1, (PULONG_PTR) SystemCallHandler35, NULL, FALSE }, +#endif + + +#if HOOK_DIROBJ + { "ZwCreateDirectoryObject", 21, -1, (PULONG_PTR) HookedNtCreateDirectoryObject, NULL, TRUE }, +#else + { "ZwCreateDirectoryObject", 21, -1, (PULONG_PTR) SystemCallHandler36, NULL, FALSE }, +#endif + + +#if HOOK_EVENT + { "ZwCreateEvent", 11, -1, (PULONG_PTR) HookedNtCreateEvent, NULL, TRUE }, + { "ZwCreateEventPair", 15, -1, (PULONG_PTR) HookedNtCreateEventPair, NULL, TRUE }, +#else + { "ZwCreateEvent", 11, -1, (PULONG_PTR) SystemCallHandler37, NULL, FALSE }, + { "ZwCreateEventPair", 15, -1, (PULONG_PTR) SystemCallHandler38, NULL, FALSE }, +#endif + + +#if HOOK_FILE + { "ZwCreateFile", 10, -1, (PULONG_PTR) HookedNtCreateFile, NULL, TRUE }, +#else + { "ZwCreateFile", 10, -1, (PULONG_PTR) SystemCallHandler39, NULL, FALSE }, +#endif + + + { "ZwCreateIoCompletion", 18, -1, (PULONG_PTR) SystemCallHandler40, NULL, FALSE }, + + +#if HOOK_JOB + { "ZwCreateJobObject", 15, -1, (PULONG_PTR) HookedNtCreateJobObject, NULL, TRUE }, +#else + { "ZwCreateJobObject", 15, -1, (PULONG_PTR) SystemCallHandler41, NULL, FALSE }, +#endif + + +//XXX ??? + { "ZwCreateJobSet", 12, -1, (PULONG_PTR) SystemCallHandler42, NULL, FALSE }, + + +#if HOOK_REGISTRY + { "ZwCreateKey", 9, -1, (PULONG_PTR) HookedNtCreateKey, NULL, TRUE }, +#else + { "ZwCreateKey", 9, -1, (PULONG_PTR) SystemCallHandler43, NULL, FALSE }, +#endif + + + { "ZwCreateKeyedEvent", 16, -1, (PULONG_PTR) SystemCallHandler44, NULL, FALSE }, + + +#if HOOK_FILE + { "ZwCreateMailslotFile", 18, -1, (PULONG_PTR) HookedNtCreateMailslotFile, NULL, TRUE }, +#else + { "ZwCreateMailslotFile", 18, -1, (PULONG_PTR) SystemCallHandler45, NULL, FALSE }, +#endif + + +#if HOOK_MUTANT + { "ZwCreateMutant", 12, -1, (PULONG_PTR) HookedNtCreateMutant, NULL, TRUE }, +#else + { "ZwCreateMutant", 12, -1, (PULONG_PTR) SystemCallHandler46, NULL, FALSE }, +#endif + + +#if HOOK_FILE + { "ZwCreateNamedPipeFile", 19, -1, (PULONG_PTR) HookedNtCreateNamedPipeFile, NULL, TRUE }, +#else + { "ZwCreateNamedPipeFile", 19, -1, (PULONG_PTR) SystemCallHandler47, NULL, FALSE }, +#endif + + + { "ZwCreatePagingFile", 16, -1, (PULONG_PTR) SystemCallHandler48, NULL, FALSE }, + + +#if HOOK_PORT + { "ZwCreatePort", 10, -1, (PULONG_PTR) HookedNtCreatePort, NULL, TRUE }, +#else + { "ZwCreatePort", 10, -1, (PULONG_PTR) SystemCallHandler49, NULL, FALSE }, +#endif + + +#if HOOK_PROCESS + { "ZwCreateProcess", 13, -1, (PULONG_PTR) HookedNtCreateProcess, NULL, TRUE }, + { "ZwCreateProcessEx", 15, -1, (PULONG_PTR) HookedNtCreateProcessEx, NULL, TRUE }, +#else + { "ZwCreateProcess", 13, -1, (PULONG_PTR) SystemCallHandler50, NULL, FALSE }, + { "ZwCreateProcessEx", 15, -1, (PULONG_PTR) SystemCallHandler51, NULL, FALSE }, +#endif + + + { "ZwCreateProfile", 13, -1, (PULONG_PTR) SystemCallHandler52, NULL, FALSE }, + + +#if HOOK_SECTION + { "ZwCreateSection", 13, -1, (PULONG_PTR) HookedNtCreateSection, NULL, TRUE }, +#else + { "ZwCreateSection", 13, -1, (PULONG_PTR) SystemCallHandler53, NULL, FALSE }, +#endif + + +#if HOOK_SEMAPHORE + { "ZwCreateSemaphore", 15, -1, (PULONG_PTR) HookedNtCreateSemaphore, NULL, TRUE }, +#else + { "ZwCreateSemaphore", 15, -1, (PULONG_PTR) SystemCallHandler54, NULL, FALSE }, +#endif + + +#if HOOK_SYMLINK + { "ZwCreateSymbolicLinkObject", 24, -1, (PULONG_PTR) HookedNtCreateSymbolicLinkObject, NULL, TRUE }, +#else + { "ZwCreateSymbolicLinkObject", 24, -1, (PULONG_PTR) SystemCallHandler55, NULL, FALSE }, +#endif + + +#if HOOK_PROCESS + { "ZwCreateThread", 12, -1, (PULONG_PTR) HookedNtCreateThread, NULL, TRUE }, +#else + { "ZwCreateThread", 12, -1, (PULONG_PTR) SystemCallHandler56, NULL, FALSE }, +#endif + + +#if HOOK_TIMER + { "ZwCreateTimer", 11, -1, (PULONG_PTR) HookedNtCreateTimer, NULL, TRUE }, +#else + { "ZwCreateTimer", 11, -1, (PULONG_PTR) SystemCallHandler57, NULL, FALSE }, +#endif + + +#if HOOK_TOKEN_ZZZ + { "ZwCreateToken", 11, -1, (PULONG_PTR) HookedNtCreateToken, NULL, TRUE }, +#else + { "ZwCreateToken", 11, -1, (PULONG_PTR) SystemCallHandler58, NULL, FALSE }, +#endif + + +#if HOOK_PORT + { "ZwCreateWaitablePort", 18, -1, (PULONG_PTR) HookedNtCreateWaitablePort, NULL, TRUE }, +#else + { "ZwCreateWaitablePort", 18, -1, (PULONG_PTR) SystemCallHandler59, NULL, FALSE }, +#endif + + +#if HOOK_DEBUG + { "ZwDebugActiveProcess", 18, -1, (PULONG_PTR) HookedNtDebugActiveProcess, NULL, TRUE }, +#else + { "ZwDebugActiveProcess", 18, -1, (PULONG_PTR) SystemCallHandler60, NULL, FALSE }, +#endif + + + { "ZwDebugContinue", 13, -1, (PULONG_PTR) SystemCallHandler61, NULL, FALSE }, + { "ZwDelayExecution", 14, -1, (PULONG_PTR) SystemCallHandler62, NULL, FALSE }, + { "ZwDeleteAtom", 10, -1, (PULONG_PTR) SystemCallHandler63, NULL, FALSE }, + { "ZwDeleteBootEntry", 15, -1, (PULONG_PTR) SystemCallHandler64, NULL, FALSE }, + { "ZwDeleteDriverEntry", 17, -1, (PULONG_PTR) SystemCallHandler65, NULL, FALSE }, + + +#if HOOK_FILE + { "ZwDeleteFile", 10, -1, (PULONG_PTR) HookedNtDeleteFile, NULL, TRUE }, +#else + { "ZwDeleteFile", 10, -1, (PULONG_PTR) SystemCallHandler66, NULL, FALSE }, +#endif + + +#if HOOK_REGISTRY + { "ZwDeleteKey", 9, -1, (PULONG_PTR) HookedNtDeleteKey, NULL, TRUE }, +#else + { "ZwDeleteKey", 9, -1, (PULONG_PTR) SystemCallHandler67, NULL, FALSE }, +#endif + + { "ZwDeleteObjectAuditAlarm", 22, -1, (PULONG_PTR) SystemCallHandler68, NULL, FALSE }, + { "ZwDeleteValueKey", 14, -1, (PULONG_PTR) SystemCallHandler69, NULL, FALSE }, +//XXX + + { "ZwDeviceIoControlFile", 19, -1, NULL, NULL, FALSE }, + { "ZwDisplayString", 13, -1, (PULONG_PTR) SystemCallHandler71, NULL, FALSE }, + { "ZwDuplicateObject", 15, -1, (PULONG_PTR) SystemCallHandler72, NULL, FALSE }, + { "ZwDuplicateToken", 14, -1, (PULONG_PTR) SystemCallHandler73, NULL, FALSE }, + { "ZwEnumerateBootEntries", 20, -1, (PULONG_PTR) SystemCallHandler74, NULL, FALSE }, + { "ZwEnumerateDriverEntries", 22, -1, (PULONG_PTR) SystemCallHandler75, NULL, FALSE }, + { "ZwEnumerateKey", 12, -1, (PULONG_PTR) SystemCallHandler76, NULL, FALSE }, + { "ZwEnumerateSystemEnvironmentValuesEx", 34, -1, (PULONG_PTR) SystemCallHandler77, NULL, FALSE }, + { "ZwEnumerateValueKey", 17, -1, (PULONG_PTR) SystemCallHandler78, NULL, FALSE }, +//XXX + + + { "ZwExtendSection", 13, -1, (PULONG_PTR) SystemCallHandler79, NULL, FALSE }, + { "ZwFilterToken", 11, -1, (PULONG_PTR) SystemCallHandler80, NULL, FALSE }, + + +#if HOOK_ATOM + { "ZwFindAtom", 8, -1, (PULONG_PTR) HookedNtFindAtom, NULL, TRUE }, +#else + { "ZwFindAtom", 8, -1, (PULONG_PTR) SystemCallHandler81, NULL, FALSE }, +#endif + + + { "ZwFlushBuffersFile", 16, -1, (PULONG_PTR) SystemCallHandler82, NULL, FALSE }, + { "ZwFlushInstructionCache", 21, -1, (PULONG_PTR) SystemCallHandler83, NULL, FALSE }, + { "ZwFlushKey", 8, -1, (PULONG_PTR) SystemCallHandler84, NULL, FALSE }, + { "ZwFlushVirtualMemory", 18, -1, (PULONG_PTR) SystemCallHandler85, NULL, FALSE }, + { "ZwFlushWriteBuffer", 16, -1, (PULONG_PTR) SystemCallHandler86, NULL, FALSE }, + { "ZwFreeUserPhysicalPages", 21, -1, (PULONG_PTR) SystemCallHandler87, NULL, FALSE }, + { "ZwFreeVirtualMemory", 17, -1, (PULONG_PTR) SystemCallHandler88, NULL, FALSE }, + { "ZwFsControlFile", 13, -1, (PULONG_PTR) SystemCallHandler89, NULL, FALSE }, + { "ZwGetContextThread", 16, -1, (PULONG_PTR) SystemCallHandler90, NULL, FALSE }, + { "ZwGetCurrentProcessorNumber", 25, -1, (PULONG_PTR) SystemCallHandler91, NULL, FALSE }, + { "ZwGetDevicePowerState", 19, -1, (PULONG_PTR) SystemCallHandler92, NULL, FALSE }, + { "ZwGetPlugPlayEvent", 16, -1, (PULONG_PTR) SystemCallHandler93, NULL, FALSE }, + { "ZwGetWriteWatch", 13, -1, (PULONG_PTR) SystemCallHandler94, NULL, FALSE }, + +//XXX + { "ZwImpersonateAnonymousToken", 25, -1, (PULONG_PTR) SystemCallHandler95, NULL, FALSE }, + { "ZwImpersonateClientOfPort", 23, -1, (PULONG_PTR) SystemCallHandler96, NULL, FALSE }, + { "ZwImpersonateThread", 17, -1, (PULONG_PTR) SystemCallHandler97, NULL, FALSE }, + + { "ZwInitializeRegistry", 18, -1, (PULONG_PTR) SystemCallHandler98, NULL, FALSE }, + { "ZwInitiatePowerAction", 19, -1, (PULONG_PTR) SystemCallHandler99, NULL, FALSE }, + { "ZwIsProcessInJob", 14, -1, (PULONG_PTR) SystemCallHandler100, NULL, FALSE }, + { "ZwIsSystemResumeAutomatic", 23, -1, (PULONG_PTR) SystemCallHandler101, NULL, FALSE }, + { "ZwListenPort", 10, -1, (PULONG_PTR) SystemCallHandler102, NULL, FALSE }, + + +#if HOOK_DRIVEROBJ + { "ZwLoadDriver", 10, -1, (PULONG_PTR) HookedNtLoadDriver, NULL, TRUE }, +#else + { "ZwLoadDriver", 10, -1, (PULONG_PTR) SystemCallHandler103, NULL, FALSE }, +#endif + + + { "ZwLoadKey", 7, -1, (PULONG_PTR) SystemCallHandler104, NULL, FALSE }, + { "ZwLoadKey2", 8, -1, (PULONG_PTR) SystemCallHandler105, NULL, FALSE }, + { "ZwLoadKeyEx", 9, -1, (PULONG_PTR) SystemCallHandler106, NULL, FALSE }, + { "ZwLockFile", 8, -1, (PULONG_PTR) SystemCallHandler107, NULL, FALSE }, + { "ZwLockProductActivationKeys", 25, -1, (PULONG_PTR) SystemCallHandler108, NULL, FALSE }, + { "ZwLockRegistryKey", 15, -1, (PULONG_PTR) SystemCallHandler109, NULL, FALSE }, + { "ZwLockVirtualMemory", 17, -1, (PULONG_PTR) SystemCallHandler110, NULL, FALSE }, + { "ZwMakePermanentObject", 19, -1, (PULONG_PTR) SystemCallHandler111, NULL, FALSE }, + { "ZwMakeTemporaryObject", 19, -1, (PULONG_PTR) SystemCallHandler112, NULL, FALSE }, + { "ZwMapUserPhysicalPages", 20, -1, (PULONG_PTR) SystemCallHandler113, NULL, FALSE }, + { "ZwMapUserPhysicalPagesScatter", 27, -1, (PULONG_PTR) SystemCallHandler114, NULL, FALSE }, + + +#if HOOK_SECTION_ZZZ + { "ZwMapViewOfSection", 16, -1, (PULONG_PTR) HookedNtMapViewOfSection, NULL, TRUE }, +#else + { "ZwMapViewOfSection", 16, -1, (PULONG_PTR) SystemCallHandler115, NULL, FALSE }, +#endif + + + { "ZwModifyBootEntry", 15, -1, (PULONG_PTR) SystemCallHandler116, NULL, FALSE }, + { "ZwModifyDriverEntry", 17, -1, (PULONG_PTR) SystemCallHandler117, NULL, FALSE }, + { "ZwNotifyChangeDirectoryFile", 25, -1, (PULONG_PTR) SystemCallHandler118, NULL, FALSE }, + { "ZwNotifyChangeKey", 15, -1, (PULONG_PTR) SystemCallHandler119, NULL, FALSE }, + { "ZwNotifyChangeMultipleKeys", 24, -1, (PULONG_PTR) SystemCallHandler120, NULL, FALSE }, + + +#if HOOK_DIROBJ + { "ZwOpenDirectoryObject", 19, -1, (PULONG_PTR) HookedNtOpenDirectoryObject, NULL, TRUE }, +#else + { "ZwOpenDirectoryObject", 19, -1, (PULONG_PTR) SystemCallHandler121, NULL, FALSE }, +#endif + + +#if HOOK_EVENT + { "ZwOpenEvent", 9, -1, (PULONG_PTR) HookedNtOpenEvent, NULL, TRUE }, + { "ZwOpenEventPair", 13, -1, (PULONG_PTR) HookedNtOpenEventPair, NULL, TRUE }, +#else + { "ZwOpenEvent", 9, -1, (PULONG_PTR) SystemCallHandler122, NULL, FALSE }, + { "ZwOpenEventPair", 13, -1, (PULONG_PTR) SystemCallHandler123, NULL, FALSE }, +#endif + + +#if HOOK_FILE + { "ZwOpenFile", 8, -1, (PULONG_PTR) HookedNtOpenFile, NULL, TRUE }, +#else + { "ZwOpenFile", 8, -1, (PULONG_PTR) SystemCallHandler124, NULL, FALSE }, +#endif + + { "ZwOpenIoCompletion", 16, -1, (PULONG_PTR) SystemCallHandler125, NULL, FALSE }, + + +#if HOOK_JOB + { "ZwOpenJobObject", 13, -1, (PULONG_PTR) HookedNtOpenJobObject, NULL, TRUE }, +#else + { "ZwOpenJobObject", 13, -1, (PULONG_PTR) SystemCallHandler126, NULL, FALSE }, +#endif + + +#if HOOK_REGISTRY + { "ZwOpenKey", 7, -1, (PULONG_PTR) HookedNtOpenKey, NULL, TRUE }, +#else + { "ZwOpenKey", 7, -1, (PULONG_PTR) SystemCallHandler127, NULL, FALSE }, +#endif + + + { "ZwOpenKeyedEvent", 14, -1, (PULONG_PTR) SystemCallHandler128, NULL, FALSE }, + + +#if HOOK_MUTANT + { "ZwOpenMutant", 10, -1, (PULONG_PTR) HookedNtOpenMutant, NULL, TRUE }, +#else + { "ZwOpenMutant", 10, -1, (PULONG_PTR) SystemCallHandler129, NULL, FALSE }, +#endif + + + { "ZwOpenObjectAuditAlarm", 20, -1, (PULONG_PTR) SystemCallHandler130, NULL, FALSE }, + + +#if HOOK_PROCESS + { "ZwOpenProcess", 11, -1, (PULONG_PTR) HookedNtOpenProcess, NULL, TRUE }, +#else + { "ZwOpenProcess", 11, -1, (PULONG_PTR) SystemCallHandler131, NULL, FALSE }, +#endif + + +#if HOOK_TOKEN_ZZZ + { "ZwOpenProcessToken", 16, -1, (PULONG_PTR) HookedNtOpenProcessToken, NULL, TRUE }, + { "ZwOpenProcessTokenEx", 18, -1, (PULONG_PTR) HookedNtOpenProcessTokenEx, NULL, TRUE }, +#else + { "ZwOpenProcessToken", 16, -1, (PULONG_PTR) SystemCallHandler132, NULL, FALSE }, + { "ZwOpenProcessTokenEx", 18, -1, (PULONG_PTR) SystemCallHandler133, NULL, FALSE }, +#endif + + +#if HOOK_SECTION + { "ZwOpenSection", 11, -1, (PULONG_PTR) HookedNtOpenSection, NULL, TRUE }, +#else + { "ZwOpenSection", 11, -1, (PULONG_PTR) SystemCallHandler134, NULL, FALSE }, +#endif + + +#if HOOK_SEMAPHORE + { "ZwOpenSemaphore", 13, -1, (PULONG_PTR) HookedNtOpenSemaphore, NULL, TRUE }, +#else + { "ZwOpenSemaphore", 13, -1, (PULONG_PTR) SystemCallHandler135, NULL, FALSE }, +#endif + + +#if HOOK_SYMLINK_ZZZ + { "ZwOpenSymbolicLinkObject", 22, -1, (PULONG_PTR) HookedNtOpenSymbolicLinkObject, NULL, TRUE }, +#else + { "ZwOpenSymbolicLinkObject", 22, -1, (PULONG_PTR) SystemCallHandler136, NULL, FALSE }, +#endif + + +#if HOOK_PROCESS + { "ZwOpenThread", 10, -1, (PULONG_PTR) HookedNtOpenThread, NULL, TRUE }, +#else + { "ZwOpenThread", 10, -1, (PULONG_PTR) SystemCallHandler137, NULL, FALSE }, +#endif + + +#if HOOK_TOKEN_ZZZ + { "ZwOpenThreadToken", 15, -1, (PULONG_PTR) HookedNtOpenThreadToken, NULL, TRUE }, + { "ZwOpenThreadTokenEx", 17, -1, (PULONG_PTR) HookedNtOpenThreadTokenEx, NULL, TRUE }, +#else + { "ZwOpenThreadToken", 15, -1, (PULONG_PTR) SystemCallHandler138, NULL, FALSE }, + { "ZwOpenThreadTokenEx", 17, -1, (PULONG_PTR) SystemCallHandler139, NULL, FALSE }, +#endif + + +#if HOOK_TIMER + { "ZwOpenTimer", 9, -1, (PULONG_PTR) HookedNtOpenTimer, NULL, TRUE }, +#else + { "ZwOpenTimer", 9, -1, (PULONG_PTR) SystemCallHandler140, NULL, FALSE }, +#endif + + + { "ZwPlugPlayControl", 15, -1, (PULONG_PTR) SystemCallHandler141, NULL, FALSE }, + { "ZwPowerInformation", 16, -1, (PULONG_PTR) SystemCallHandler142, NULL, FALSE }, + { "ZwPrivilegeCheck", 14, -1, (PULONG_PTR) SystemCallHandler143, NULL, FALSE }, + { "ZwPrivilegeObjectAuditAlarm", 25, -1, (PULONG_PTR) SystemCallHandler144, NULL, FALSE }, + { "ZwPrivilegedServiceAuditAlarm", 27, -1, (PULONG_PTR) SystemCallHandler145, NULL, FALSE }, + { "ZwProtectVirtualMemory", 20, -1, (PULONG_PTR) SystemCallHandler146, NULL, FALSE }, + { "ZwPulseEvent", 10, -1, (PULONG_PTR) SystemCallHandler147, NULL, FALSE }, + + +#if HOOK_FILE + { "ZwQueryAttributesFile", 19, -1, (PULONG_PTR) HookedNtQueryAttributesFile, NULL, TRUE }, +#else + { "ZwQueryAttributesFile", 19, -1, (PULONG_PTR) SystemCallHandler148, NULL, FALSE }, +#endif + + + { "ZwQueryBootEntryOrder", 19, -1, (PULONG_PTR) SystemCallHandler149, NULL, FALSE }, + { "ZwQueryBootOptions", 16, -1, (PULONG_PTR) SystemCallHandler150, NULL, FALSE }, + { "ZwQueryDebugFilterState", 21, -1, (PULONG_PTR) SystemCallHandler151, NULL, FALSE }, + { "ZwQueryDefaultLocale", 18, -1, (PULONG_PTR) SystemCallHandler152, NULL, FALSE }, + { "ZwQueryDefaultUILanguage", 22, -1, (PULONG_PTR) SystemCallHandler153, NULL, FALSE }, + + +#if FILE_HOOK_ZZZ + { "ZwQueryDirectoryFile", 18, -1, (PULONG_PTR) HookedNtQueryDirectoryFile, NULL, TRUE }, +#else + { "ZwQueryDirectoryFile", 18, -1, (PULONG_PTR) SystemCallHandler154, NULL, FALSE }, +#endif + + + { "ZwQueryDirectoryObject", 20, -1, (PULONG_PTR) SystemCallHandler155, NULL, FALSE }, + { "ZwQueryDriverEntryOrder", 21, -1, (PULONG_PTR) SystemCallHandler156, NULL, FALSE }, + { "ZwQueryEaFile", 11, -1, (PULONG_PTR) SystemCallHandler157, NULL, FALSE }, + { "ZwQueryEvent", 10, -1, (PULONG_PTR) SystemCallHandler158, NULL, FALSE }, + + +#if HOOK_FILE + { "ZwQueryFullAttributesFile", 23, -1, (PULONG_PTR) HookedNtQueryFullAttributesFile, NULL, TRUE }, +#else + { "ZwQueryFullAttributesFile", 23, -1, (PULONG_PTR) SystemCallHandler159, NULL, FALSE }, +#endif + + + { "ZwQueryInformationAtom", 20, -1, (PULONG_PTR) SystemCallHandler160, NULL, FALSE }, + { "ZwQueryInformationFile", 20, -1, (PULONG_PTR) SystemCallHandler161, NULL, FALSE }, + { "ZwQueryInformationJobObject", 25, -1, (PULONG_PTR) SystemCallHandler162, NULL, FALSE }, + { "ZwQueryInformationPort", 20, -1, (PULONG_PTR) SystemCallHandler163, NULL, FALSE }, + { "ZwQueryInformationProcess", 23, -1, (PULONG_PTR) SystemCallHandler164, NULL, FALSE }, + { "ZwQueryInformationThread", 22, -1, (PULONG_PTR) SystemCallHandler165, NULL, FALSE }, + { "ZwQueryInformationToken", 21, -1, (PULONG_PTR) SystemCallHandler166, NULL, FALSE }, + { "ZwQueryInstallUILanguage", 22, -1, (PULONG_PTR) SystemCallHandler167, NULL, FALSE }, + { "ZwQueryIntervalProfile", 20, -1, (PULONG_PTR) SystemCallHandler168, NULL, FALSE }, + { "ZwQueryIoCompletion", 17, -1, (PULONG_PTR) SystemCallHandler169, NULL, FALSE }, + { "ZwQueryKey", 8, -1, (PULONG_PTR) SystemCallHandler170, NULL, FALSE }, + { "ZwQueryMultipleValueKey", 21, -1, (PULONG_PTR) SystemCallHandler171, NULL, FALSE }, + { "ZwQueryMutant", 11, -1, (PULONG_PTR) SystemCallHandler172, NULL, FALSE }, + { "ZwQueryObject", 11, -1, (PULONG_PTR) SystemCallHandler173, NULL, FALSE }, + { "ZwQueryOpenSubKeys", 16, -1, (PULONG_PTR) SystemCallHandler174, NULL, FALSE }, + { "ZwQueryOpenSubKeysEx", 18, -1, (PULONG_PTR) SystemCallHandler175, NULL, FALSE }, + { "ZwQueryPerformanceCounter", 23, -1, (PULONG_PTR) SystemCallHandler176, NULL, FALSE }, + { "ZwQueryPortInformationProcess", 27, -1, (PULONG_PTR) SystemCallHandler177, NULL, FALSE }, + { "ZwQueryQuotaInformationFile", 25, -1, (PULONG_PTR) SystemCallHandler178, NULL, FALSE }, + { "ZwQuerySection", 12, -1, (PULONG_PTR) SystemCallHandler179, NULL, FALSE }, + { "ZwQuerySecurityObject", 19, -1, (PULONG_PTR) SystemCallHandler180, NULL, FALSE }, + { "ZwQuerySemaphore", 14, -1, (PULONG_PTR) SystemCallHandler181, NULL, FALSE }, + { "ZwQuerySymbolicLinkObject", 23, -1, (PULONG_PTR) SystemCallHandler182, NULL, FALSE }, + { "ZwQuerySystemEnvironmentValue", 27, -1, (PULONG_PTR) SystemCallHandler183, NULL, FALSE }, + { "ZwQuerySystemEnvironmentValueEx", 29, -1, (PULONG_PTR) SystemCallHandler184, NULL, FALSE }, + { "ZwQuerySystemInformation", 22, -1, (PULONG_PTR) SystemCallHandler185, NULL, FALSE }, + { "ZwQuerySystemTime", 15, -1, (PULONG_PTR) SystemCallHandler186, NULL, FALSE }, + { "ZwQueryTimer", 10, -1, (PULONG_PTR) SystemCallHandler187, NULL, FALSE }, + { "ZwQueryTimerResolution", 20, -1, (PULONG_PTR) SystemCallHandler188, NULL, FALSE }, + + +#if HOOK_REGISTRY_ZZZ + { "ZwQueryValueKey", 13, -1, (PULONG_PTR) HookedNtQueryValueKey, NULL, TRUE }, +#else + { "ZwQueryValueKey", 13, -1, (PULONG_PTR) SystemCallHandler189, NULL, FALSE }, +#endif + + + { "ZwQueryVirtualMemory", 18, -1, (PULONG_PTR) SystemCallHandler190, NULL, FALSE }, + { "ZwQueryVolumeInformationFile", 26, -1, (PULONG_PTR) SystemCallHandler191, NULL, FALSE }, + { "ZwQueueApcThread", 14, -1, (PULONG_PTR) SystemCallHandler192, NULL, FALSE }, + + +//XXX should we not mediate these calls? they are only raised during an error and we might +// not encounter them during "learning" phase? can these be abused otherwise? + { "ZwRaiseException", 14, -1, (PULONG_PTR) SystemCallHandler193, NULL, FALSE }, + { "ZwRaiseHardError", 14, -1, (PULONG_PTR) SystemCallHandler194, NULL, FALSE }, + + /* don't mediate for performance reasons, requires a valid handle anyway */ + { "ZwReadFile", 8, -1, NULL, NULL, FALSE }, + { "ZwReadFileScatter", 15, -1, NULL, NULL, FALSE }, + { "ZwReadRequestData", 15, -1, NULL, NULL, FALSE }, + { "ZwReadVirtualMemory", 17, -1, NULL, NULL, FALSE }, + + { "ZwRegisterThreadTerminatePort", 27, -1, (PULONG_PTR) SystemCallHandler199, NULL, FALSE }, + { "ZwReleaseKeyedEvent", 17, -1, (PULONG_PTR) SystemCallHandler200, NULL, FALSE }, + { "ZwReleaseMutant", 13, -1, (PULONG_PTR) SystemCallHandler201, NULL, FALSE }, + { "ZwReleaseSemaphore", 16, -1, (PULONG_PTR) SystemCallHandler202, NULL, FALSE }, + { "ZwRemoveIoCompletion", 18, -1, (PULONG_PTR) SystemCallHandler203, NULL, FALSE }, + { "ZwRemoveProcessDebug", 18, -1, (PULONG_PTR) SystemCallHandler204, NULL, FALSE }, + +//XXX + { "ZwRenameKey", 9, -1, (PULONG_PTR) SystemCallHandler205, NULL, FALSE }, + { "ZwReplaceKey", 10, -1, (PULONG_PTR) SystemCallHandler206, NULL, FALSE }, + + + { "ZwReplyPort", 9, -1, (PULONG_PTR) SystemCallHandler207, NULL, FALSE }, + { "ZwReplyWaitReceivePort", 20, -1, (PULONG_PTR) SystemCallHandler208, NULL, FALSE }, + { "ZwReplyWaitReceivePortEx", 22, -1, (PULONG_PTR) SystemCallHandler209, NULL, FALSE }, + { "ZwReplyWaitReplyPort", 18, -1, (PULONG_PTR) SystemCallHandler210, NULL, FALSE }, + { "ZwRequestDeviceWakeup", 19, -1, (PULONG_PTR) SystemCallHandler211, NULL, FALSE }, + { "ZwRequestPort", 11, -1, (PULONG_PTR) SystemCallHandler212, NULL, FALSE }, + { "ZwRequestWaitReplyPort", 20, -1, (PULONG_PTR) SystemCallHandler213, NULL, FALSE }, + { "ZwRequestWakeupLatency", 20, -1, (PULONG_PTR) SystemCallHandler214, NULL, FALSE }, + { "ZwResetEvent", 10, -1, (PULONG_PTR) SystemCallHandler215, NULL, FALSE }, + { "ZwResetWriteWatch", 15, -1, (PULONG_PTR) SystemCallHandler216, NULL, FALSE }, + { "ZwRestoreKey", 10, -1, (PULONG_PTR) SystemCallHandler217, NULL, FALSE }, + { "ZwResumeProcess", 13, -1, (PULONG_PTR) SystemCallHandler218, NULL, FALSE }, + { "ZwResumeThread", 12, -1, (PULONG_PTR) SystemCallHandler219, NULL, FALSE }, + { "ZwSaveKey", 7, -1, (PULONG_PTR) SystemCallHandler220, NULL, FALSE }, + { "ZwSaveKeyEx", 9, -1, (PULONG_PTR) SystemCallHandler221, NULL, FALSE }, + { "ZwSaveMergedKeys", 14, -1, (PULONG_PTR) SystemCallHandler222, NULL, FALSE }, + + +#if HOOK_PORT + { "ZwSecureConnectPort", 17, -1, (PULONG_PTR) HookedNtSecureConnectPort, NULL, TRUE }, +#else + { "ZwSecureConnectPort", 17, -1, (PULONG_PTR) SystemCallHandler223, NULL, FALSE }, +#endif + + + { "ZwSetBootEntryOrder", 17, -1, (PULONG_PTR) SystemCallHandler224, NULL, FALSE }, + { "ZwSetBootOptions", 14, -1, (PULONG_PTR) SystemCallHandler225, NULL, FALSE }, + +//XXX + { "ZwSetContextThread", 16, -1, (PULONG_PTR) SystemCallHandler226, NULL, FALSE }, + + { "ZwSetDebugFilterState", 19, -1, (PULONG_PTR) SystemCallHandler227, NULL, FALSE }, + { "ZwSetDefaultHardErrorPort", 23, -1, (PULONG_PTR) SystemCallHandler228, NULL, FALSE }, + { "ZwSetDefaultLocale", 16, -1, (PULONG_PTR) SystemCallHandler229, NULL, FALSE }, + { "ZwSetDefaultUILanguage", 20, -1, (PULONG_PTR) SystemCallHandler230, NULL, FALSE }, + { "ZwSetDriverEntryOrder", 19, -1, (PULONG_PTR) SystemCallHandler231, NULL, FALSE }, + { "ZwSetEaFile", 9, -1, (PULONG_PTR) SystemCallHandler232, NULL, FALSE }, + { "ZwSetEvent", 8, -1, (PULONG_PTR) SystemCallHandler233, NULL, FALSE }, + { "ZwSetEventBoostPriority", 21, -1, (PULONG_PTR) SystemCallHandler234, NULL, FALSE }, + { "ZwSetHighEventPair", 16, -1, (PULONG_PTR) SystemCallHandler235, NULL, FALSE }, + { "ZwSetHighWaitLowEventPair", 23, -1, (PULONG_PTR) SystemCallHandler236, NULL, FALSE }, + { "ZwSetInformationDebugObject", 25, -1, (PULONG_PTR) SystemCallHandler237, NULL, FALSE }, + +#if HOOK_FILE + { "ZwSetInformationFile", 18, -1, (PULONG_PTR) HookedNtSetInformationFile, NULL, TRUE }, +#else + { "ZwSetInformationFile", 18, -1, (PULONG_PTR) SystemCallHandler238, NULL, FALSE }, +#endif + + { "ZwSetInformationJobObject", 23, -1, (PULONG_PTR) SystemCallHandler239, NULL, FALSE }, + { "ZwSetInformationKey", 17, -1, (PULONG_PTR) SystemCallHandler240, NULL, FALSE }, + { "ZwSetInformationObject", 20, -1, (PULONG_PTR) SystemCallHandler241, NULL, FALSE }, + { "ZwSetInformationProcess", 21, -1, (PULONG_PTR) SystemCallHandler242, NULL, FALSE }, + { "ZwSetInformationThread", 20, -1, (PULONG_PTR) SystemCallHandler243, NULL, FALSE }, + + +#if HOOK_TOKEN + { "ZwSetInformationToken", 19, -1, (PULONG_PTR) HookedNtSetInformationToken, NULL, TRUE }, +#else + { "ZwSetInformationToken", 19, -1, (PULONG_PTR) SystemCallHandler244, NULL, FALSE }, +#endif + + + { "ZwSetIntervalProfile", 18, -1, (PULONG_PTR) SystemCallHandler245, NULL, FALSE }, + { "ZwSetIoCompletion", 15, -1, (PULONG_PTR) SystemCallHandler246, NULL, FALSE }, + + +#if HOOK_VDM + { "ZwSetLdtEntries", 13, -1, (PULONG_PTR) HookedNtSetLdtEntries, NULL, TRUE }, +#else + { "ZwSetLdtEntries", 13, -1, (PULONG_PTR) SystemCallHandler247, NULL, FALSE }, +#endif + + + { "ZwSetLowEventPair", 15, -1, (PULONG_PTR) SystemCallHandler248, NULL, FALSE }, + { "ZwSetLowWaitHighEventPair", 23, -1, (PULONG_PTR) SystemCallHandler249, NULL, FALSE }, + { "ZwSetQuotaInformationFile", 23, -1, (PULONG_PTR) SystemCallHandler250, NULL, FALSE }, + { "ZwSetSecurityObject", 17, -1, (PULONG_PTR) SystemCallHandler251, NULL, FALSE }, + { "ZwSetSystemEnvironmentValue", 25, -1, (PULONG_PTR) SystemCallHandler252, NULL, FALSE }, // XXX intel hal supports only 1 variable LastKnownGood + { "ZwSetSystemEnvironmentValueEx", 27, -1, (PULONG_PTR) SystemCallHandler253, NULL, FALSE }, + + +#if HOOK_SYSINFO + { "ZwSetSystemInformation", 20, -1, (PULONG_PTR) HookedNtSetSystemInformation, NULL, TRUE }, +#else + { "ZwSetSystemInformation", 20, -1, (PULONG_PTR) SystemCallHandler254, NULL, FALSE }, +#endif + + + { "ZwSetSystemPowerState", 19, -1, (PULONG_PTR) SystemCallHandler255, NULL, FALSE }, + + +#if HOOK_TIME + { "ZwSetSystemTime", 13, -1, (PULONG_PTR) HookedNtSetSystemTime, NULL, TRUE }, +#else + { "ZwSetSystemTime", 13, -1, (PULONG_PTR) SystemCallHandler256, NULL, FALSE }, +#endif + + + { "ZwSetThreadExecutionState", 23, -1, (PULONG_PTR) SystemCallHandler257, NULL, FALSE }, + { "ZwSetTimer", 8, -1, (PULONG_PTR) SystemCallHandler258, NULL, FALSE }, + + +#if 0 //HOOK_TIME + { "ZwSetTimerResolution", 18, -1, (PULONG_PTR) HookedNtSetTimerResolution, NULL, TRUE }, +#else + { "ZwSetTimerResolution", 18, -1, (PULONG_PTR) SystemCallHandler259, NULL, FALSE }, +#endif + + + { "ZwSetUuidSeed", 11, -1, (PULONG_PTR) SystemCallHandler260, NULL, FALSE }, + + +#if HOOK_REGISTRY_ZZZ + { "ZwSetValueKey", 11, -1, (PULONG_PTR) HookedNtSetValueKey, NULL, TRUE }, +#else + { "ZwSetValueKey", 11, -1, (PULONG_PTR) SystemCallHandler261, NULL, FALSE }, +#endif + + + { "ZwSetVolumeInformationFile", 24, -1, (PULONG_PTR) SystemCallHandler262, NULL, FALSE }, + +//XXX + { "ZwShutdownSystem", 14, -1, (PULONG_PTR) SystemCallHandler263, NULL, FALSE }, + + { "ZwSignalAndWaitForSingleObject", 28, -1, (PULONG_PTR) SystemCallHandler264, NULL, FALSE }, + { "ZwStartProfile", 12, -1, (PULONG_PTR) SystemCallHandler265, NULL, FALSE }, + { "ZwStopProfile", 11, -1, (PULONG_PTR) SystemCallHandler266, NULL, FALSE }, + { "ZwSuspendProcess", 14, -1, (PULONG_PTR) SystemCallHandler267, NULL, FALSE }, + { "ZwSuspendThread", 13, -1, (PULONG_PTR) SystemCallHandler268, NULL, FALSE }, + { "ZwSystemDebugControl", 18, -1, (PULONG_PTR) SystemCallHandler269, NULL, FALSE }, + +//XXX + { "ZwTerminateJobObject", 18, -1, (PULONG_PTR) SystemCallHandler270, NULL, FALSE }, + { "ZwTerminateProcess", 16, -1, (PULONG_PTR) SystemCallHandler271, NULL, FALSE }, + { "ZwTerminateThread", 15, -1, (PULONG_PTR) SystemCallHandler272, NULL, FALSE }, + + { "ZwTestAlert", 9, -1, (PULONG_PTR) SystemCallHandler273, NULL, FALSE }, + { "ZwTraceEvent", 10, -1, (PULONG_PTR) SystemCallHandler274, NULL, FALSE }, + { "ZwTranslateFilePath", 17, -1, (PULONG_PTR) SystemCallHandler275, NULL, FALSE }, + + +#if HOOK_DRIVEROBJ_ZZZ + { "ZwUnloadDriver", 12, -1, (PULONG_PTR) HookedNtUnloadDriver, NULL, TRUE }, +#else + { "ZwUnloadDriver", 12, -1, (PULONG_PTR) SystemCallHandler276, NULL, FALSE }, +#endif + + + { "ZwUnloadKey", 9, -1, (PULONG_PTR) SystemCallHandler277, NULL, FALSE }, + { "ZwUnloadKey2", 10, -1, (PULONG_PTR) SystemCallHandler278, NULL, FALSE }, + { "ZwUnloadKeyEx", 11, -1, (PULONG_PTR) SystemCallHandler279, NULL, FALSE }, + { "ZwUnlockFile", 10, -1, (PULONG_PTR) SystemCallHandler280, NULL, FALSE }, + { "ZwUnlockVirtualMemory", 19, -1, (PULONG_PTR) SystemCallHandler281, NULL, FALSE }, + { "ZwUnmapViewOfSection", 18, -1, (PULONG_PTR) SystemCallHandler282, NULL, FALSE }, + + +#if HOOK_VDM + { "ZwVdmControl", 10, -1, (PULONG_PTR) HookedNtVdmControl, NULL, TRUE }, +#else + { "ZwVdmControl", 10, -1, (PULONG_PTR) SystemCallHandler283, NULL, FALSE }, +#endif + + + { "ZwWaitForDebugEvent", 17, -1, (PULONG_PTR) SystemCallHandler284, NULL, FALSE }, + { "ZwWaitForKeyedEvent", 17, -1, (PULONG_PTR) SystemCallHandler285, NULL, FALSE }, + { "ZwWaitForMultipleObjects", 22, -1, (PULONG_PTR) SystemCallHandler286, NULL, FALSE }, + { "ZwWaitForSingleObject", 19, -1, (PULONG_PTR) SystemCallHandler287, NULL, FALSE }, + { "ZwWaitHighEventPair", 17, -1, (PULONG_PTR) SystemCallHandler288, NULL, FALSE }, + { "ZwWaitLowEventPair", 16, -1, (PULONG_PTR) SystemCallHandler289, NULL, FALSE }, + + /* don't mediate for performance reasons, requires a valid handle anyway */ + { "ZwWriteFile", 9, -1, NULL, NULL, FALSE }, + { "ZwWriteFileGather", 15, -1, NULL, NULL, FALSE }, + { "ZwWriteRequestData", 16, -1, NULL, NULL, FALSE }, + { "ZwWriteVirtualMemory", 18, -1, NULL, NULL, FALSE }, + { "ZwYieldExecution", 14, -1, NULL, NULL, FALSE }, +}; + + + +ACTION_TYPE +VerifySystemServiceCall(USHORT SystemServiceNumber) +{ + ACTION_TYPE Action = ACTION_NONE; + PIMAGE_PID_ENTRY p; + PPOLICY_RULE PolicyRule; + + +// LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("system call %x %x %s. irql %d", ZwCalls[SystemServiceNumber].OriginalFunction, ZwCalls[SystemServiceNumber].ServiceIDNumber, ZwCalls[SystemServiceNumber].ZwName, KeGetCurrentIrql())); + + + if (KeGetCurrentIrql() != PASSIVE_LEVEL) + return ACTION_NONE; + + + if (LearningMode) + { + AddRule(RULE_SYSCALL, ZwCalls[SystemServiceNumber].ZwName, 0); + return ACTION_NONE; + } + + + /* verify policy and user return address? */ + + p = FindImagePidEntry(CURRENT_PROCESS_PID, 0); + + if (p == NULL) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("VerifySystemServiceCall: FindImagePidEntry(%d) failed\n", CURRENT_PROCESS_PID)); + return ACTION_NONE; + } +//XXX p->SecPolicy spinlock is not acquired + PolicyRule = p->SecPolicy.RuleList[ RULE_SYSCALL ]; + if (PolicyRule) + { + /* Calculate the bit array ULONG we need to read (bit array consists of a bunch of ULONGs) */ + ULONG UlongCount = SystemServiceNumber >> UlongBitShift; + + /* Choose the correct bit array ULONG */ + PULONG BitArray = &PolicyRule->ServiceBitArray[0] + UlongCount; + + ULONG BitOffset = SystemServiceNumber - (UlongCount << UlongBitShift); + + /* read the bit */ + Action = *BitArray & ( 1 << BitOffset ) ? ACTION_PERMIT : ACTION_DENY; + + if (Action == ACTION_DENY) + { + LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("denying system call %x %x %s\n", ZwCalls[SystemServiceNumber].OriginalFunction, ZwCalls[SystemServiceNumber].ServiceIDNumber, ZwCalls[SystemServiceNumber].ZwName)); + Action = ACTION_PERMIT; + } + } + + return Action; +} + + +//XXX can we get rid of pushfd / popfd ? +//XXX move to i386.c? +#define SYSCALL_HANDLER(num) \ + __declspec(naked) void SystemCallHandler##num() \ + { \ + _asm pushad \ + _asm { pushfd } \ + if (0 && VerifySystemServiceCall(num) == ACTION_DENY)\ + { \ + _asm popfd \ + _asm popad \ + _asm mov eax, 0xC0000022 /*STATUS_ACCESS_DENIED*/ \ + _asm ret \ + } \ + _asm popfd \ + _asm popad \ + _asm jmp dword ptr [ ZwCalls + num*20 + 12 ] \ +/* _asm jmp ZwCalls[num].OriginalFunction */\ + } + +/* +//XXX can find out dynamically the amount of stack space to clean up by looking at Zw* ret instruction + if (PolicyCheck(RULE_SYSCALL, NULL, 0) == ACTION_DENY) \ + { \ + _asm mov eax, 0xC0000022 \ + _asm ret \ + } \ +*/ + + +SYSCALL_HANDLER(0) SYSCALL_HANDLER(1) SYSCALL_HANDLER(2) SYSCALL_HANDLER(3) SYSCALL_HANDLER(4) +SYSCALL_HANDLER(5) SYSCALL_HANDLER(6) SYSCALL_HANDLER(7) SYSCALL_HANDLER(8) SYSCALL_HANDLER(9) +SYSCALL_HANDLER(10) SYSCALL_HANDLER(11) SYSCALL_HANDLER(12) SYSCALL_HANDLER(13) SYSCALL_HANDLER(14) +SYSCALL_HANDLER(15) SYSCALL_HANDLER(16) SYSCALL_HANDLER(17) SYSCALL_HANDLER(18) SYSCALL_HANDLER(19) +SYSCALL_HANDLER(20) SYSCALL_HANDLER(21) SYSCALL_HANDLER(22) SYSCALL_HANDLER(23) SYSCALL_HANDLER(24) +SYSCALL_HANDLER(25) SYSCALL_HANDLER(26) SYSCALL_HANDLER(27) SYSCALL_HANDLER(28) SYSCALL_HANDLER(29) +SYSCALL_HANDLER(30) SYSCALL_HANDLER(31) SYSCALL_HANDLER(32) SYSCALL_HANDLER(33) SYSCALL_HANDLER(34) +SYSCALL_HANDLER(35) SYSCALL_HANDLER(36) SYSCALL_HANDLER(37) SYSCALL_HANDLER(38) SYSCALL_HANDLER(39) +SYSCALL_HANDLER(40) SYSCALL_HANDLER(41) SYSCALL_HANDLER(42) SYSCALL_HANDLER(43) SYSCALL_HANDLER(44) +SYSCALL_HANDLER(45) SYSCALL_HANDLER(46) SYSCALL_HANDLER(47) SYSCALL_HANDLER(48) SYSCALL_HANDLER(49) +SYSCALL_HANDLER(50) SYSCALL_HANDLER(51) SYSCALL_HANDLER(52) SYSCALL_HANDLER(53) SYSCALL_HANDLER(54) +SYSCALL_HANDLER(55) SYSCALL_HANDLER(56) SYSCALL_HANDLER(57) SYSCALL_HANDLER(58) SYSCALL_HANDLER(59) +SYSCALL_HANDLER(60) SYSCALL_HANDLER(61) SYSCALL_HANDLER(62) SYSCALL_HANDLER(63) SYSCALL_HANDLER(64) +SYSCALL_HANDLER(65) SYSCALL_HANDLER(66) SYSCALL_HANDLER(67) SYSCALL_HANDLER(68) SYSCALL_HANDLER(69) +SYSCALL_HANDLER(70) SYSCALL_HANDLER(71) SYSCALL_HANDLER(72) SYSCALL_HANDLER(73) SYSCALL_HANDLER(74) +SYSCALL_HANDLER(75) SYSCALL_HANDLER(76) SYSCALL_HANDLER(77) SYSCALL_HANDLER(78) SYSCALL_HANDLER(79) +SYSCALL_HANDLER(80) SYSCALL_HANDLER(81) SYSCALL_HANDLER(82) SYSCALL_HANDLER(83) SYSCALL_HANDLER(84) +SYSCALL_HANDLER(85) SYSCALL_HANDLER(86) SYSCALL_HANDLER(87) SYSCALL_HANDLER(88) SYSCALL_HANDLER(89) +SYSCALL_HANDLER(90) SYSCALL_HANDLER(91) SYSCALL_HANDLER(92) SYSCALL_HANDLER(93) SYSCALL_HANDLER(94) +SYSCALL_HANDLER(95) SYSCALL_HANDLER(96) SYSCALL_HANDLER(97) SYSCALL_HANDLER(98) SYSCALL_HANDLER(99) +SYSCALL_HANDLER(100) SYSCALL_HANDLER(101) SYSCALL_HANDLER(102) SYSCALL_HANDLER(103) SYSCALL_HANDLER(104) +SYSCALL_HANDLER(105) SYSCALL_HANDLER(106) SYSCALL_HANDLER(107) SYSCALL_HANDLER(108) SYSCALL_HANDLER(109) +SYSCALL_HANDLER(110) SYSCALL_HANDLER(111) SYSCALL_HANDLER(112) SYSCALL_HANDLER(113) SYSCALL_HANDLER(114) +SYSCALL_HANDLER(115) SYSCALL_HANDLER(116) SYSCALL_HANDLER(117) SYSCALL_HANDLER(118) SYSCALL_HANDLER(119) +SYSCALL_HANDLER(120) SYSCALL_HANDLER(121) SYSCALL_HANDLER(122) SYSCALL_HANDLER(123) SYSCALL_HANDLER(124) +SYSCALL_HANDLER(125) SYSCALL_HANDLER(126) SYSCALL_HANDLER(127) SYSCALL_HANDLER(128) SYSCALL_HANDLER(129) +SYSCALL_HANDLER(130) SYSCALL_HANDLER(131) SYSCALL_HANDLER(132) SYSCALL_HANDLER(133) SYSCALL_HANDLER(134) +SYSCALL_HANDLER(135) SYSCALL_HANDLER(136) SYSCALL_HANDLER(137) SYSCALL_HANDLER(138) SYSCALL_HANDLER(139) +SYSCALL_HANDLER(140) SYSCALL_HANDLER(141) SYSCALL_HANDLER(142) SYSCALL_HANDLER(143) SYSCALL_HANDLER(144) +SYSCALL_HANDLER(145) SYSCALL_HANDLER(146) SYSCALL_HANDLER(147) SYSCALL_HANDLER(148) SYSCALL_HANDLER(149) +SYSCALL_HANDLER(150) SYSCALL_HANDLER(151) SYSCALL_HANDLER(152) SYSCALL_HANDLER(153) SYSCALL_HANDLER(154) +SYSCALL_HANDLER(155) SYSCALL_HANDLER(156) SYSCALL_HANDLER(157) SYSCALL_HANDLER(158) SYSCALL_HANDLER(159) +SYSCALL_HANDLER(160) SYSCALL_HANDLER(161) SYSCALL_HANDLER(162) SYSCALL_HANDLER(163) SYSCALL_HANDLER(164) +SYSCALL_HANDLER(165) SYSCALL_HANDLER(166) SYSCALL_HANDLER(167) SYSCALL_HANDLER(168) SYSCALL_HANDLER(169) +SYSCALL_HANDLER(170) SYSCALL_HANDLER(171) SYSCALL_HANDLER(172) SYSCALL_HANDLER(173) SYSCALL_HANDLER(174) +SYSCALL_HANDLER(175) SYSCALL_HANDLER(176) SYSCALL_HANDLER(177) SYSCALL_HANDLER(178) SYSCALL_HANDLER(179) +SYSCALL_HANDLER(180) SYSCALL_HANDLER(181) SYSCALL_HANDLER(182) SYSCALL_HANDLER(183) SYSCALL_HANDLER(184) +SYSCALL_HANDLER(185) SYSCALL_HANDLER(186) SYSCALL_HANDLER(187) SYSCALL_HANDLER(188) SYSCALL_HANDLER(189) +SYSCALL_HANDLER(190) SYSCALL_HANDLER(191) SYSCALL_HANDLER(192) SYSCALL_HANDLER(193) SYSCALL_HANDLER(194) +SYSCALL_HANDLER(195) SYSCALL_HANDLER(196) SYSCALL_HANDLER(197) SYSCALL_HANDLER(198) SYSCALL_HANDLER(199) +SYSCALL_HANDLER(200) SYSCALL_HANDLER(201) SYSCALL_HANDLER(202) SYSCALL_HANDLER(203) SYSCALL_HANDLER(204) +SYSCALL_HANDLER(205) SYSCALL_HANDLER(206) SYSCALL_HANDLER(207) SYSCALL_HANDLER(208) SYSCALL_HANDLER(209) +SYSCALL_HANDLER(210) SYSCALL_HANDLER(211) SYSCALL_HANDLER(212) SYSCALL_HANDLER(213) SYSCALL_HANDLER(214) +SYSCALL_HANDLER(215) SYSCALL_HANDLER(216) SYSCALL_HANDLER(217) SYSCALL_HANDLER(218) SYSCALL_HANDLER(219) +SYSCALL_HANDLER(220) SYSCALL_HANDLER(221) SYSCALL_HANDLER(222) SYSCALL_HANDLER(223) SYSCALL_HANDLER(224) +SYSCALL_HANDLER(225) SYSCALL_HANDLER(226) SYSCALL_HANDLER(227) SYSCALL_HANDLER(228) SYSCALL_HANDLER(229) +SYSCALL_HANDLER(230) SYSCALL_HANDLER(231) SYSCALL_HANDLER(232) SYSCALL_HANDLER(233) SYSCALL_HANDLER(234) +SYSCALL_HANDLER(235) SYSCALL_HANDLER(236) SYSCALL_HANDLER(237) SYSCALL_HANDLER(238) SYSCALL_HANDLER(239) +SYSCALL_HANDLER(240) SYSCALL_HANDLER(241) SYSCALL_HANDLER(242) SYSCALL_HANDLER(243) SYSCALL_HANDLER(244) +SYSCALL_HANDLER(245) SYSCALL_HANDLER(246) SYSCALL_HANDLER(247) SYSCALL_HANDLER(248) SYSCALL_HANDLER(249) +SYSCALL_HANDLER(250) SYSCALL_HANDLER(251) SYSCALL_HANDLER(252) SYSCALL_HANDLER(253) SYSCALL_HANDLER(254) +SYSCALL_HANDLER(255) SYSCALL_HANDLER(256) SYSCALL_HANDLER(257) SYSCALL_HANDLER(258) SYSCALL_HANDLER(259) +SYSCALL_HANDLER(260) SYSCALL_HANDLER(261) SYSCALL_HANDLER(262) SYSCALL_HANDLER(263) SYSCALL_HANDLER(264) +SYSCALL_HANDLER(265) SYSCALL_HANDLER(266) SYSCALL_HANDLER(267) SYSCALL_HANDLER(268) SYSCALL_HANDLER(269) +SYSCALL_HANDLER(270) SYSCALL_HANDLER(271) SYSCALL_HANDLER(272) SYSCALL_HANDLER(273) SYSCALL_HANDLER(274) +SYSCALL_HANDLER(275) SYSCALL_HANDLER(276) SYSCALL_HANDLER(277) SYSCALL_HANDLER(278) SYSCALL_HANDLER(279) +SYSCALL_HANDLER(280) SYSCALL_HANDLER(281) SYSCALL_HANDLER(282) SYSCALL_HANDLER(283) SYSCALL_HANDLER(284) +SYSCALL_HANDLER(285) SYSCALL_HANDLER(286) SYSCALL_HANDLER(287) SYSCALL_HANDLER(288) SYSCALL_HANDLER(289) +SYSCALL_HANDLER(290) SYSCALL_HANDLER(291) SYSCALL_HANDLER(292) SYSCALL_HANDLER(293) SYSCALL_HANDLER(294) + + + +/* + * InitSyscallsHooks() + * + * Description: + * Find the correct global syscall table indeces for all system calls + * (initialize "OriginalFunction" pointers). Must be called at PASSIVE_LEVEL IRQL + * in order to access pageable memory. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitSyscallsHooks() +{ + int i; + + + ZwCallsNumber = sizeof(ZwCalls) / sizeof(ZwCalls[0]); + + for (i = 0; i < ZwCallsNumber; i++) + { + //XXX this can be optimized to return index directly?! this way when we restore syscalls back we don't have to do the same process again??!?! + ZwCalls[i].ServiceIDNumber = (USHORT) FindZwFunctionIndex(ZwCalls[i].ZwName); + } + + + return TRUE; +} + + + +/* + * InstallSyscallsHooks() + * + * Description: + * Mediate various system services. Called at DISPATCH_LEVEL IRQL. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InstallSyscallsHooks() +{ + int i; + + + ZwCallsNumber = sizeof(ZwCalls) / sizeof(ZwCalls[0]); + + for (i = 0; i < ZwCallsNumber; i++) + { + if (ZwCalls[i].HookFunction != NULL && + (HOOK_SYSCALLS || LearningMode || ZwCalls[i].Hijacked == TRUE)) + { + if (ZwCalls[i].ServiceIDNumber != (USHORT) -1) + { + ZwCalls[i].OriginalFunction = HookSystemServiceByIndex(ZwCalls[i].ServiceIDNumber, + ZwCalls[i].HookFunction); + ZwCalls[i].Hijacked = TRUE; + } + } + } + + + return TRUE; +} + + + +/* + * RemoveSyscallsHooks() + * + * Description: + * Restores all the original system service function pointers. + * + * NOTE: Called once during driver unload (DriverUnload()). + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +void +RemoveSyscallsHooks() +{ + int i; + + + for (i = 0; i < ZwCallsNumber; i++) + { + // restore hijacked system calls + if (ZwCalls[i].Hijacked == TRUE && ZwCalls[i].OriginalFunction != NULL && ZwCalls[i].ServiceIDNumber != -1) + { +// LOG(LOG_SS_HOOKPROC, LOG_PRIORITY_DEBUG, ("Restoring syscall %d %x\n", i, i)); + + HookSystemServiceByIndex(ZwCalls[i].ServiceIDNumber, ZwCalls[i].OriginalFunction); + } + } +} diff --git a/hookproc.h b/hookproc.h new file mode 100644 index 0000000..7756d23 --- /dev/null +++ b/hookproc.h @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * hookproc.h + * + * Abstract: + * + * This module definies various types used by service operation (system call) hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 16-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __HOOKPROC_H__ +#define __HOOKPROC_H__ + + +#include "userland.h" + + +/* should the following calls be intercepted? */ + +#define HOOK_EVENT 1 +#define HOOK_FILE 1 +#define HOOK_DIROBJ 1 +#define HOOK_JOB 1 +#define HOOK_NETWORK 1 +#define HOOK_MUTANT 1 +#define HOOK_PORT 1 +#define HOOK_PROCESS 1 +#define HOOK_REGISTRY 1 +#define HOOK_SECTION 1 +#define HOOK_SEMAPHORE 1 +#define HOOK_SYMLINK 1 +#define HOOK_SYSINFO 1 +#define HOOK_TIME 1 +#define HOOK_TIMER 1 +#define HOOK_TOKEN 1 +#define HOOK_DRIVEROBJ 1 +#define HOOK_ATOM 1 +#define HOOK_VDM 1 +#define HOOK_SYSCALLS 0 +#define HOOK_DEBUG 1 +#define HOOK_MEDIA 1 +#define HOOK_BOPROT 0 + + +#pragma pack(push, 1) +typedef struct _SERVICE_TABLE_DESCRIPTOR { + + PULONG ServiceTableBase; /* table of function pointers */ + PVOID ServiceCounterTable; /* used in checked build only */ + ULONG NumberOfServices; /* number of services in this table */ + /* extra LONG on IA64 goes here */ + PVOID ParamTableBase; /* number of parameters */ + +} SERVICE_TABLE_DESCRIPTOR, *PSERVICE_TABLE_DESCRIPTOR; +#pragma pack(pop) + + +/* + * The Service Descriptor Table index (4 bytes following the mov opcode) + * + * The index format is as follows: + * + * Leading 18 bits are all zeroes + * Following 2 bits are system service table index (3 bits on Win64) + * Following 12 bits are service number + */ + +#define SERVICE_TABLE_INDEX_BITS 2 +#define NUMBER_SERVICE_TABLES (1 << SERVICE_TABLE_INDEX_BITS) + +#define SERVICE_ID_NUMBER_BITS 12 +#define SERVICE_ID_NUMBER_MASK ((1 << SERVICE_ID_NUMBER_BITS) - 1) + + +/* + * The kernel's service descriptor table, which is used to find the address + * of the service dispatch tables to use for a service ID. + * + * Descriptor 0 is used for core services (NTDLL) + * Descriptor 1 is used for GUI services (WIN32K) + * Descriptors 2 and 3 are unused on current versions of Windows NT. + */ + +__declspec(dllimport) SERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable[NUMBER_SERVICE_TABLES]; + + +/* + * not exported + */ + +//PSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTableShadow; + + + +void SystemCallHandler0(); void SystemCallHandler1(); void SystemCallHandler2(); void SystemCallHandler3(); +void SystemCallHandler4(); void SystemCallHandler5(); void SystemCallHandler6(); void SystemCallHandler7(); +void SystemCallHandler8(); void SystemCallHandler9(); void SystemCallHandler10(); void SystemCallHandler11(); +void SystemCallHandler12(); void SystemCallHandler13(); void SystemCallHandler14(); void SystemCallHandler15(); +void SystemCallHandler16(); void SystemCallHandler17(); void SystemCallHandler18(); void SystemCallHandler19(); +void SystemCallHandler20(); void SystemCallHandler21(); void SystemCallHandler22(); void SystemCallHandler23(); +void SystemCallHandler24(); void SystemCallHandler25(); void SystemCallHandler26(); void SystemCallHandler27(); +void SystemCallHandler28(); void SystemCallHandler29(); void SystemCallHandler30(); void SystemCallHandler31(); +void SystemCallHandler32(); void SystemCallHandler33(); void SystemCallHandler34(); void SystemCallHandler35(); +void SystemCallHandler36(); void SystemCallHandler37(); void SystemCallHandler38(); void SystemCallHandler39(); +void SystemCallHandler40(); void SystemCallHandler41(); void SystemCallHandler42(); void SystemCallHandler43(); +void SystemCallHandler44(); void SystemCallHandler45(); void SystemCallHandler46(); void SystemCallHandler47(); +void SystemCallHandler48(); void SystemCallHandler49(); void SystemCallHandler50(); void SystemCallHandler51(); +void SystemCallHandler52(); void SystemCallHandler53(); void SystemCallHandler54(); void SystemCallHandler55(); +void SystemCallHandler56(); void SystemCallHandler57(); void SystemCallHandler58(); void SystemCallHandler59(); +void SystemCallHandler60(); void SystemCallHandler61(); void SystemCallHandler62(); void SystemCallHandler63(); +void SystemCallHandler64(); void SystemCallHandler65(); void SystemCallHandler66(); void SystemCallHandler67(); +void SystemCallHandler68(); void SystemCallHandler69(); void SystemCallHandler70(); void SystemCallHandler71(); +void SystemCallHandler72(); void SystemCallHandler73(); void SystemCallHandler74(); void SystemCallHandler75(); +void SystemCallHandler76(); void SystemCallHandler77(); void SystemCallHandler78(); void SystemCallHandler79(); +void SystemCallHandler80(); void SystemCallHandler81(); void SystemCallHandler82(); void SystemCallHandler83(); +void SystemCallHandler84(); void SystemCallHandler85(); void SystemCallHandler86(); void SystemCallHandler87(); +void SystemCallHandler88(); void SystemCallHandler89(); void SystemCallHandler90(); void SystemCallHandler91(); +void SystemCallHandler92(); void SystemCallHandler93(); void SystemCallHandler94(); void SystemCallHandler95(); +void SystemCallHandler96(); void SystemCallHandler97(); void SystemCallHandler98(); void SystemCallHandler99(); +void SystemCallHandler100(); void SystemCallHandler101(); void SystemCallHandler102(); void SystemCallHandler103(); +void SystemCallHandler104(); void SystemCallHandler105(); void SystemCallHandler106(); void SystemCallHandler107(); +void SystemCallHandler108(); void SystemCallHandler109(); void SystemCallHandler110(); void SystemCallHandler111(); +void SystemCallHandler112(); void SystemCallHandler113(); void SystemCallHandler114(); void SystemCallHandler115(); +void SystemCallHandler116(); void SystemCallHandler117(); void SystemCallHandler118(); void SystemCallHandler119(); +void SystemCallHandler120(); void SystemCallHandler121(); void SystemCallHandler122(); void SystemCallHandler123(); +void SystemCallHandler124(); void SystemCallHandler125(); void SystemCallHandler126(); void SystemCallHandler127(); +void SystemCallHandler128(); void SystemCallHandler129(); void SystemCallHandler130(); void SystemCallHandler131(); +void SystemCallHandler132(); void SystemCallHandler133(); void SystemCallHandler134(); void SystemCallHandler135(); +void SystemCallHandler136(); void SystemCallHandler137(); void SystemCallHandler138(); void SystemCallHandler139(); +void SystemCallHandler140(); void SystemCallHandler141(); void SystemCallHandler142(); void SystemCallHandler143(); +void SystemCallHandler144(); void SystemCallHandler145(); void SystemCallHandler146(); void SystemCallHandler147(); +void SystemCallHandler148(); void SystemCallHandler149(); void SystemCallHandler150(); void SystemCallHandler151(); +void SystemCallHandler152(); void SystemCallHandler153(); void SystemCallHandler154(); void SystemCallHandler155(); +void SystemCallHandler156(); void SystemCallHandler157(); void SystemCallHandler158(); void SystemCallHandler159(); +void SystemCallHandler160(); void SystemCallHandler161(); void SystemCallHandler162(); void SystemCallHandler163(); +void SystemCallHandler164(); void SystemCallHandler165(); void SystemCallHandler166(); void SystemCallHandler167(); +void SystemCallHandler168(); void SystemCallHandler169(); void SystemCallHandler170(); void SystemCallHandler171(); +void SystemCallHandler172(); void SystemCallHandler173(); void SystemCallHandler174(); void SystemCallHandler175(); +void SystemCallHandler176(); void SystemCallHandler177(); void SystemCallHandler178(); void SystemCallHandler179(); +void SystemCallHandler180(); void SystemCallHandler181(); void SystemCallHandler182(); void SystemCallHandler183(); +void SystemCallHandler184(); void SystemCallHandler185(); void SystemCallHandler186(); void SystemCallHandler187(); +void SystemCallHandler188(); void SystemCallHandler189(); void SystemCallHandler190(); void SystemCallHandler191(); +void SystemCallHandler192(); void SystemCallHandler193(); void SystemCallHandler194(); void SystemCallHandler195(); +void SystemCallHandler196(); void SystemCallHandler197(); void SystemCallHandler198(); void SystemCallHandler199(); +void SystemCallHandler200(); void SystemCallHandler201(); void SystemCallHandler202(); void SystemCallHandler203(); +void SystemCallHandler204(); void SystemCallHandler205(); void SystemCallHandler206(); void SystemCallHandler207(); +void SystemCallHandler208(); void SystemCallHandler209(); void SystemCallHandler210(); void SystemCallHandler211(); +void SystemCallHandler212(); void SystemCallHandler213(); void SystemCallHandler214(); void SystemCallHandler215(); +void SystemCallHandler216(); void SystemCallHandler217(); void SystemCallHandler218(); void SystemCallHandler219(); +void SystemCallHandler220(); void SystemCallHandler221(); void SystemCallHandler222(); void SystemCallHandler223(); +void SystemCallHandler224(); void SystemCallHandler225(); void SystemCallHandler226(); void SystemCallHandler227(); +void SystemCallHandler228(); void SystemCallHandler229(); void SystemCallHandler230(); void SystemCallHandler231(); +void SystemCallHandler232(); void SystemCallHandler233(); void SystemCallHandler234(); void SystemCallHandler235(); +void SystemCallHandler236(); void SystemCallHandler237(); void SystemCallHandler238(); void SystemCallHandler239(); +void SystemCallHandler240(); void SystemCallHandler241(); void SystemCallHandler242(); void SystemCallHandler243(); +void SystemCallHandler244(); void SystemCallHandler245(); void SystemCallHandler246(); void SystemCallHandler247(); +void SystemCallHandler248(); void SystemCallHandler249(); void SystemCallHandler250(); void SystemCallHandler251(); +void SystemCallHandler252(); void SystemCallHandler253(); void SystemCallHandler254(); void SystemCallHandler255(); +void SystemCallHandler256(); void SystemCallHandler257(); void SystemCallHandler258(); void SystemCallHandler259(); +void SystemCallHandler260(); void SystemCallHandler261(); void SystemCallHandler262(); void SystemCallHandler263(); +void SystemCallHandler264(); void SystemCallHandler265(); void SystemCallHandler266(); void SystemCallHandler267(); +void SystemCallHandler268(); void SystemCallHandler269(); void SystemCallHandler270(); void SystemCallHandler271(); +void SystemCallHandler272(); void SystemCallHandler273(); void SystemCallHandler274(); void SystemCallHandler275(); +void SystemCallHandler276(); void SystemCallHandler277(); void SystemCallHandler278(); void SystemCallHandler279(); +void SystemCallHandler280(); void SystemCallHandler281(); void SystemCallHandler282(); void SystemCallHandler283(); +void SystemCallHandler284(); void SystemCallHandler285(); void SystemCallHandler286(); void SystemCallHandler287(); +void SystemCallHandler288(); void SystemCallHandler289(); void SystemCallHandler290(); void SystemCallHandler291(); +void SystemCallHandler292(); void SystemCallHandler293(); void SystemCallHandler294(); + + + +// XXX +// SystemCallHandler macro depends on the size of this structure and the offset of the OriginalFunction! + +extern struct _ZwCalls +{ + PCHAR ZwName; // System call name + USHORT ZwNameLength; // System call name length + USHORT ServiceIDNumber; // System call index (filled in at runtime) + PULONG_PTR HookFunction; // Address of the hijacking function (function that will be called instead of the original system call) + PULONG_PTR OriginalFunction; // PlaceHolder for the address of the original syscall address + BOOLEAN Hijacked; // Flag indicating whether we already hijacked this system call + // or whether this is a special system service that needs to be hijacked initially +}; + +extern struct _ZwCalls ZwCalls[]; + + +#define ZW_ADD_ATOM_INDEX 8 + +#define ZW_ADJUST_TOKEN_INDEX 12 + +#define ZW_CONNECT_PORT_INDEX 33 + +#define ZW_CREATE_DIRECTORYOBJECT_INDEX 36 +#define ZW_CREATE_EVENT_INDEX 37 +#define ZW_CREATE_EVENT_PAIR_INDEX 38 +#define ZW_CREATE_FILE_INDEX 39 + +#define ZW_CREATE_JOBOBJECT_INDEX 41 + +#define ZW_CREATE_KEY_INDEX 43 + +#define ZW_CREATE_MAILSLOTFILE_INDEX 45 +#define ZW_CREATE_MUTANT_INDEX 46 +#define ZW_CREATE_NAMEDPIPEFILE_INDEX 47 + +#define ZW_CREATE_PORT_INDEX 49 +#define ZW_CREATE_PROCESS_INDEX 50 +#define ZW_CREATE_PROCESSEX_INDEX 51 + +#define ZW_CREATE_SECTION_INDEX 53 +#define ZW_CREATE_SEMAPHORE_INDEX 54 +#define ZW_CREATE_SYMLINK_INDEX 55 +#define ZW_CREATE_THREAD_INDEX 56 +#define ZW_CREATE_TIMER_INDEX 57 +#define ZW_CREATE_TOKEN_INDEX 58 +#define ZW_CREATE_WAITPORT_INDEX 59 +#define ZW_DEBUG_ACTIVEPROCESS_INDEX 60 + +#define ZW_DELETE_FILE_INDEX 66 +#define ZW_DELETE_KEY_INDEX 67 + +#define ZW_FIND_ATOM_INDEX 81 + +#define ZW_LOAD_DRIVER_INDEX 103 + +#define ZW_MAPVIEW_SECTION_INDEX 115 + +#define ZW_OPEN_DIRECTORYOBJECT_INDEX 121 +#define ZW_OPEN_EVENT_INDEX 122 +#define ZW_OPEN_EVENT_PAIR_INDEX 123 +#define ZW_OPEN_FILE_INDEX 124 + +#define ZW_OPEN_JOBOBJECT_INDEX 126 +#define ZW_OPEN_KEY_INDEX 127 + +#define ZW_OPEN_MUTANT_INDEX 129 + +#define ZW_OPEN_PROCESS_INDEX 131 + +#define ZW_OPEN_SECTION_INDEX 134 +#define ZW_OPEN_SEMAPHORE_INDEX 135 +#define ZW_OPEN_SYMLINK_INDEX 136 +#define ZW_OPEN_THREAD_INDEX 137 + +#define ZW_OPEN_TIMER_INDEX 140 + +#define ZW_QUERY_ATTRIBUTES_FILE_INDEX 148 + +#define ZW_QUERY_DIRECTORYFILE_INDEX 154 + +#define ZW_QUERY_FULLATTR_FILE_INDEX 159 + +#define ZW_QUERY_VALUE_KEY_INDEX 189 + +#define ZW_SECURECONNECT_PORT_INDEX 223 + +#define ZW_SET_INFO_FILE_INDEX 238 + +#define ZW_SET_INFO_TOKEN_INDEX 244 + +#define ZW_SET_LDT_ENTRIES_INDEX 247 + +#define ZW_SET_SYSTEM_INFORMATION_INDEX 254 + +#define ZW_SET_SYSTEM_TIME_INDEX 256 + +#define ZW_SET_TIMER_RESOLUTION_INDEX 259 + +#define ZW_SET_VALUE_KEY_INDEX 261 + +#define ZW_UNLOAD_DRIVER_INDEX 276 + +#define ZW_VDM_CONTROL_INDEX 283 + + +/* + * make sure we don't try to unload the driver while a system call is in progress + * still not atomic but we shouldn't be unloading this driver in any case + */ + +#if DBG + +extern int HookedRoutineRunning; +#define HOOK_ROUTINE_ENTER() NTSTATUS rc; ACTION_TYPE Action; InterlockedIncrement(&HookedRoutineRunning); +#define HOOK_ROUTINE_EXIT(status) { InterlockedDecrement(&HookedRoutineRunning); return ((status)); } + +extern int HookedTDIRunning; +#define HOOK_TDI_ENTER() NTSTATUS rc; ACTION_TYPE Action; InterlockedIncrement(&HookedTDIRunning); +#define HOOK_TDI_ENTER_NORC() InterlockedIncrement(&HookedTDIRunning); +#define HOOK_TDI_EXIT(status) { InterlockedDecrement(&HookedTDIRunning); return ((status)); } + + +#else + + +#define HOOK_ROUTINE_ENTER() NTSTATUS rc; ACTION_TYPE Action; +#define HOOK_ROUTINE_EXIT(status) { return ((status)); } + +#define HOOK_TDI_ENTER() NTSTATUS rc; ACTION_TYPE Action; +#define HOOK_TDI_ENTER_NORC() +#define HOOK_TDI_EXIT(status) { return ((status)); } + +#endif + + +/* + * Various macros used by most of the hooking routines + */ + +#define POLICY_CHECK_OPTYPE_NAME(OBJECTTYPE, OPTYPE) \ + while (KeGetPreviousMode() == UserMode) { \ + UCHAR OpType = (OPTYPE); \ + PWSTR PolicyFilename = NULL; \ + USHORT PolicyLinenumber = 0; \ + UCHAR RuleNumber = 0; \ + LOG(LOG_SS_##OBJECTTYPE, LOG_PRIORITY_VERBOSE, ("%d %s: %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, OBJECTTYPE##NAME)); \ + Action = PolicyCheck(RULE_##OBJECTTYPE, OBJECTTYPE##NAME, OpType, &RuleNumber, &PolicyFilename, &PolicyLinenumber);\ + if (Action & ACTION_ASK) \ + { \ + LOG(LOG_SS_##OBJECTTYPE, LOG_PRIORITY_DEBUG, ("%d %s: (ask) access to %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, OBJECTTYPE##NAME)); \ + /*XXX GetPathFromOA(ObjectAttributes, OBJECTTYPE##NAME, MAX_PATH, DO_NOT_RESOLVE_LINKS);*/ \ + Action = IssueUserlandAskUserRequest(RULE_##OBJECTTYPE, OpType, OBJECTTYPE##NAME); \ + } \ + if ((Action & ACTION_QUIETDENY) == ACTION_QUIETDENY) \ + { \ + LOG(LOG_SS_##OBJECTTYPE, LOG_PRIORITY_VERBOSE, ("%d %s: quitely denying access to %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, OBJECTTYPE##NAME)); \ + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); \ + } \ + else if (Action & ACTION_DENY) \ + { \ + LOG(LOG_SS_##OBJECTTYPE, LOG_PRIORITY_VERBOSE, ("%d %s: denying access to %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, OBJECTTYPE##NAME)); \ + LogAlert(ALERT_SS_##OBJECTTYPE, OpType, RuleNumber, Action, \ + GetObjectAccessAlertPriority(ALERT_SS_##OBJECTTYPE, OpType, Action), PolicyFilename, PolicyLinenumber, OBJECTTYPE##NAME);\ + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); \ + } \ + else if (Action & ACTION_LOG) \ + { \ + LOG(LOG_SS_##OBJECTTYPE, LOG_PRIORITY_VERBOSE, ("%d %s: (log) access to %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, OBJECTTYPE##NAME)); \ + LogAlert(ALERT_SS_##OBJECTTYPE, OpType, RuleNumber, Action, \ + GetObjectAccessAlertPriority(ALERT_SS_##OBJECTTYPE, OpType, Action), PolicyFilename, PolicyLinenumber, OBJECTTYPE##NAME);\ + } \ + break; \ + } + + +#define POLICY_CHECK_OPTYPE(OBJECTTYPE, OPTYPE) \ + if (KeGetPreviousMode() == UserMode && GetPathFromOA(ObjectAttributes, OBJECTTYPE##NAME, MAX_PATH, RESOLVE_LINKS) )\ + { \ + UCHAR OpType = (OPTYPE); \ + PWSTR PolicyFilename = NULL; \ + USHORT PolicyLinenumber = 0; \ + UCHAR RuleNumber = 0; \ + LOG(LOG_SS_##OBJECTTYPE, LOG_PRIORITY_VERBOSE, ("%d %s: %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, OBJECTTYPE##NAME)); \ + Action = PolicyCheck(RULE_##OBJECTTYPE, OBJECTTYPE##NAME, OpType, &RuleNumber, &PolicyFilename, &PolicyLinenumber);\ + if (Action & ACTION_ASK) \ + { \ + LOG(LOG_SS_##OBJECTTYPE, LOG_PRIORITY_DEBUG, ("%d %s: (ask) access to %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, OBJECTTYPE##NAME)); \ + GetPathFromOA(ObjectAttributes, OBJECTTYPE##NAME, MAX_PATH, DO_NOT_RESOLVE_LINKS); \ + Action = IssueUserlandAskUserRequest(RULE_##OBJECTTYPE, OpType, OBJECTTYPE##NAME); \ + } \ + if ((Action & ACTION_QUIETDENY) == ACTION_QUIETDENY) \ + { \ + LOG(LOG_SS_##OBJECTTYPE, LOG_PRIORITY_VERBOSE, ("%d %s: quitely denying access to %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, OBJECTTYPE##NAME)); \ + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); \ + } \ + else if (Action & ACTION_DENY) \ + { \ + LOG(LOG_SS_##OBJECTTYPE, LOG_PRIORITY_VERBOSE, ("%d %s: denying access to %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, OBJECTTYPE##NAME)); \ + GetPathFromOA(ObjectAttributes, OBJECTTYPE##NAME, MAX_PATH, DO_NOT_RESOLVE_LINKS); \ + LogAlert(ALERT_SS_##OBJECTTYPE, OpType, RuleNumber, Action, \ + GetObjectAccessAlertPriority(ALERT_SS_##OBJECTTYPE, OpType, Action), PolicyFilename, PolicyLinenumber, OBJECTTYPE##NAME);\ + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); \ + } \ + else if (Action & ACTION_LOG) \ + { \ + LOG(LOG_SS_##OBJECTTYPE, LOG_PRIORITY_VERBOSE, ("%d %s: (log) access to %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, OBJECTTYPE##NAME)); \ + GetPathFromOA(ObjectAttributes, OBJECTTYPE##NAME, MAX_PATH, DO_NOT_RESOLVE_LINKS); \ + LogAlert(ALERT_SS_##OBJECTTYPE, OpType, RuleNumber, Action, \ + GetObjectAccessAlertPriority(ALERT_SS_##OBJECTTYPE, OpType, Action), PolicyFilename, PolicyLinenumber, OBJECTTYPE##NAME);\ + } \ + } + + +#define POLICY_CHECK(OBJECTTYPE) POLICY_CHECK_OPTYPE(OBJECTTYPE, Get_##OBJECTTYPE##_OperationType(DesiredAccess)) + + + +#define HOOK_ROUTINE_START_OPTYPE(OBJECTTYPE, OPTYPE) \ + CHAR OBJECTTYPE##NAME[MAX_PATH]; \ + HOOK_ROUTINE_ENTER(); \ + if (LearningMode == FALSE) \ + { \ + POLICY_CHECK_OPTYPE(OBJECTTYPE, OPTYPE); \ + } + + +#define HOOK_ROUTINE_START(OBJECTTYPE) HOOK_ROUTINE_START_OPTYPE(OBJECTTYPE, Get_##OBJECTTYPE##_OperationType(DesiredAccess)) + + +#define HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(OBJECTTYPE, OBJECTNAME, OPTYPE) \ + if (LearningMode == TRUE /*&& NT_SUCCESS(rc)*/) \ + { \ + if (OBJECTNAME) \ + { \ + AddRule(RULE_##OBJECTTYPE, OBJECTTYPE##NAME, OPTYPE); \ + } \ + else \ + { \ + /*LOG(LOG_SS_##OBJECTTYPE, LOG_PRIORITY_DEBUG, ("%d %s: GetPathFromOA() failed. status=%x\n", (ULONG) PsGetCurrentProcessId(), FunctionName, rc));*/ \ + } \ + } \ + HOOK_ROUTINE_EXIT(rc); + + +#define HOOK_ROUTINE_FINISH_OPTYPE(OBJECTTYPE, OPTYPE) \ + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(OBJECTTYPE, \ + GetPathFromOA(ObjectAttributes, OBJECTTYPE##NAME, MAX_PATH, RESOLVE_LINKS), \ + OPTYPE) + +#define HOOK_ROUTINE_FINISH(OBJECTTYPE) \ + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(OBJECTTYPE, \ + GetPathFromOA(ObjectAttributes, OBJECTTYPE##NAME, MAX_PATH, RESOLVE_LINKS), \ + Get_##OBJECTTYPE##_OperationType(DesiredAccess)) + + + +//#define USE_DEFAULT_HOOK_FUNCTION NULL + + +extern PCHAR NTDLL_Base; +extern int ZwCallsNumber; + + +PVOID HookSystemService(PVOID OldService, PVOID NewService); +PVOID HookSystemServiceByIndex(ULONG ServiceIDNumber, PVOID NewService); +BOOLEAN HookSystemServiceByName(PCHAR ServiceName, PULONG_PTR HookFunction); + +BOOLEAN InitSyscallsHooks(); +BOOLEAN InstallSyscallsHooks(); +void RemoveSyscallsHooks(); + +int FindZwFunctionIndex(PCSTR Name); +PVOID FindFunctionBase(PCHAR ImageBase, PCSTR Name); +ULONG FindSystemServiceNumber(PCHAR ServiceName); + + +#endif /* __HOOKPROC_H__ */ \ No newline at end of file diff --git a/i386.c b/i386.c new file mode 100644 index 0000000..1ad1ad2 --- /dev/null +++ b/i386.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * i386.c + * + * Abstract: + * + * This module implements various x86 processor dependant routines. + * + * Author: + * + * Eugene Tsyrklevich 07-Apr-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "hookproc.h" +#include "i386.h" +#include "misc.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitI386) +#endif + + +ULONG SystemAddressStart; +static ULONG SharedUserDataAddress, TssAddress; +ULONG MajorVersion, MinorVersion; + + +/* + * InitI386() + * + * Description: + * Verify that we are running on Win2k, XP or 2003 on x86 platform. + * Also initialize various i386 related variables. Since Windows Advanced & Datacenter editions + * support a boot-time option that allows 3-GB user address spaces we cannot rely + * on static addresses for predefined structures. + * + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +BOOLEAN +InitI386() +{ + CHAR Gdtr[6]; + USHORT TssOffset; + PKGDTENTRY TssGdtEntry; + + + PsGetVersion(&MajorVersion, &MinorVersion, NULL, NULL); + + + /* + * Right now we only support Windows 2000 (5.0), XP (5.1), 2003 (5.2) + */ + + if (MajorVersion != 5 || MinorVersion > 2) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("InitI386: Version = %d.%d\n", MajorVersion, MinorVersion)); + return FALSE; + } + + + /* + * Find the Shared User Data address. + */ + + if (* (PULONG) MmHighestUserAddress == 0x7FFEFFFF) + { + SharedUserDataAddress = 0x7FFE0000; + SystemAddressStart = 0x80000000; + } + else if (* (PULONG) MmHighestUserAddress == 0xBFFEFFFF) + { + SharedUserDataAddress = 0xBFFE0000; + SystemAddressStart = 0xC0000000; + } + else + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("InitI386: Unknown MmHighestUserAddress=%x\n", * (PULONG) MmHighestUserAddress)); + return FALSE; + } + + + /* + * Find the TSS address. + * + * STR - Stores the segment selector from the task register (TR) in the destination operand. The + * destination operand can be a general-purpose register or a memory location. The segment selector + * stored with this instruction points to the task state segment (TSS) for the currently running task. + * + * SGDT - Stores the content of the global descriptor table register (GDTR) in the destination operand. + * The destination operand specifies a 6-byte memory location. + */ + + _asm + { + str TssOffset + sgdt Gdtr + } + + TssGdtEntry = (PKGDTENTRY) * (PULONG) (Gdtr + 2); /* Extract the GDT address */ + (PCHAR) TssGdtEntry += TssOffset; + + TssAddress = TssGdtEntry->BaseLow | ( ((TssGdtEntry->HighWord.Bytes.BaseHi) << 24) | ((TssGdtEntry->HighWord.Bytes.BaseMid) << 16) ); + + + return TRUE; +} + + + +/* + * VerifyUserReturnAddress() + * + * Description: + * Verifies whether a specified userland return address is valid. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +VOID +VerifyUserReturnAddress() +{ + PKTSS tss = (PKTSS) TssAddress; + ULONG UserEip, UserEsp; + + + /* + * this feature is not supported on Windows 2000 as it can make + * system calls from anywhere, not just ntdll.dll (it uses int 0x2e instead of sysenter) + */ + if (MinorVersion == 0) + return; + + if (KeGetPreviousMode() != UserMode) + return; + + + // EIP is 5 DWORDs before ESP0 on stack +#define EIP_OFFSET 5 + + UserEip = * (PULONG) (tss->Esp0 - EIP_OFFSET * sizeof(DWORD)); + + +#define STACK_POINTER_OFFSET 2 + + UserEsp = * (PULONG) (tss->Esp0 - STACK_POINTER_OFFSET * sizeof(DWORD)); + UserEsp -= 4; + + + //XXX verify that the return address is not on a writable page + //(might be used with Win2K which can make calls from anywhere) + + if (UserEip < (ULONG) NTDLL_Base) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%d VerifyUserReturnAddress: Abnormal return user EIP=%x ESP0=%x (NTDLL_Base=%x)\n", (ULONG) PsGetCurrentProcessId(), UserEip, tss->Esp0, NTDLL_Base)); + + LogAlert(ALERT_SS_BOPROT, OP_INVALIDCALL, ALERT_RULE_BOPROT_INVALIDCALL, ACTION_LOG, ALERT_PRIORITY_HIGH, NULL, 0, NULL); + } +} diff --git a/i386.h b/i386.h new file mode 100644 index 0000000..803e635 --- /dev/null +++ b/i386.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * i386.h + * + * Abstract: + * + * This module definies various types and macros used by x86 specific routines. + * + * Author: + * + * Eugene Tsyrklevich 24-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __I386_H__ +#define __I386_H__ + + + +typedef struct _KTSS { + + USHORT Backlink; + USHORT Reserved0; + + ULONG Esp0; + USHORT Ss0; + USHORT Reserved1; + + ULONG NotUsed1[4]; + + ULONG CR3; + + ULONG Eip; + + ULONG NotUsed2[9]; + + USHORT Es; + USHORT Reserved2; + + USHORT Cs; + USHORT Reserved3; + + USHORT Ss; + USHORT Reserved4; + + USHORT Ds; + USHORT Reserved5; + + USHORT Fs; + USHORT Reserved6; + + USHORT Gs; + USHORT Reserved7; + + USHORT LDT; + USHORT Reserved8; + + USHORT Flags; + + USHORT IoMapBase; + + /* IO/INT MAPS go here */ + +} KTSS, *PKTSS; + + +typedef struct _KGDTENTRY { + USHORT LimitLow; + USHORT BaseLow; + union { + struct { + UCHAR BaseMid; + UCHAR Flags1; // Declare as bytes to avoid alignment + UCHAR Flags2; // Problems. + UCHAR BaseHi; + } Bytes; + struct { + ULONG BaseMid : 8; + ULONG Type : 5; + ULONG Dpl : 2; + ULONG Pres : 1; + ULONG LimitHi : 4; + ULONG Sys : 1; + ULONG Reserved_0 : 1; + ULONG Default_Big : 1; + ULONG Granularity : 1; + ULONG BaseHi : 8; + } Bits; + } HighWord; +} KGDTENTRY, *PKGDTENTRY; + + +#define INTERRUPTS_OFF() _asm { cli } +#define INTERRUPTS_ON() _asm { sti } + + +/* + * WP Write Protect (bit 16 of CR0). + * Inhibits supervisor-level procedures from writing into user-level read-only pages when set; + * allows supervisor-level procedures to write into user-level read-only pages when clear. + * This flag facilitates implementation of the copyon-write method of creating a new process (forking) + * used by operating systems such as UNIX. + */ + +#define CR0_WP_BIT (0x10000) + +#define MEMORY_PROTECTION_OFF() \ + __asm mov eax, cr0 \ + __asm and eax, NOT CR0_WP_BIT \ + __asm mov cr0, eax + +#define MEMORY_PROTECTION_ON() \ + __asm mov eax, cr0 \ + __asm or eax, CR0_WP_BIT \ + __asm mov cr0, eax + + + +/* x86 opcodes */ + +#define X86_OPCODE_PUSH 0x68 +#define X86_OPCODE_MOV_EAX_VALUE 0xB8 + +#define X86_OPCODE_CALL_EAX 0xD0FF +#define X86_OPCODE_JMP_DWORD_PTR 0x25FF + + + +/* + * Save a value on stack: + * + * push PushValue + */ + +#define ASM_PUSH(CodeAddress, PushValue) \ + * (PCHAR) (CodeAddress)++ = X86_OPCODE_PUSH; \ + * (PULONG) (CodeAddress) = (ULONG) (PushValue); \ + (PCHAR) (CodeAddress) += 4; + +/* + * Call a function: + * + * mov eax, FunctionAddress + * call eax + */ + +#define ASM_CALL(CodeAddress, FunctionAddress) \ + * (PCHAR) (CodeAddress)++ = X86_OPCODE_MOV_EAX_VALUE; \ + * (PULONG) (CodeAddress) = (ULONG) (FunctionAddress); \ + (PCHAR) (CodeAddress) += 4; \ + * ((PUSHORT) (CodeAddress))++ = X86_OPCODE_CALL_EAX; + + +/* + * Jump to a specified address: + * + * jmp dword ptr [next_4_bytes] + * *(next_4_bytes) = address + * + * NOTE XXX: this should be converted to a direct jmp address but i + * can't figure out how that instruction is encoded (opcode 0xE9) + */ + +#define ASM_JMP(CodeAddress, JmpAddress) \ + * ((PUSHORT) (CodeAddress))++ = X86_OPCODE_JMP_DWORD_PTR; \ + * (PULONG) (CodeAddress) = (ULONG) (JmpAddress); \ + (PCHAR) (CodeAddress) += 4; + + +extern ULONG SystemAddressStart; + + +BOOLEAN InitI386(); +VOID VerifyUserReturnAddress(); + + +#endif /* __I386_H__ */ \ No newline at end of file diff --git a/job.c b/job.c new file mode 100644 index 0000000..cccf224 --- /dev/null +++ b/job.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * job.c + * + * Abstract: + * + * This module implements various job object hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "job.h" +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitJobHooks) +#endif + + +fpZwCreateJobObject OriginalNtCreateJobObject = NULL; +fpZwOpenJobObject OriginalNtOpenJobObject = NULL; + + +/* + * HookedNtCreateJobObject() + * + * Description: + * This function mediates the NtCreateJobObject() system service and checks the + * provided job object name against the global and current process security policies. + * + * NOTE: ZwCreateJobObject creates or opens a job object. [NAR] + * + * Parameters: + * Those of NtCreateJobObject(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateJobObject(). + */ + +NTSTATUS +NTAPI +HookedNtCreateJobObject +( + OUT PHANDLE JobHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtCreateJobObject"; + + + HOOK_ROUTINE_START(JOB); + + + ASSERT(OriginalNtCreateJobObject); + + rc = OriginalNtCreateJobObject(JobHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(JOB); +} + + + +/* + * HookedNtOpenJobObject() + * + * Description: + * This function mediates the NtOpenJobObject() system service and checks the + * provided job object name against the global and current process security policies. + * + * NOTE: ZwOpenJobObject opens a job object. [NAR] + * + * Parameters: + * Those of NtOpenJobObject(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtOpenJobObject(). + */ + +NTSTATUS +NTAPI +HookedNtOpenJobObject +( + OUT PHANDLE JobHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtOpenJobObject"; + + + HOOK_ROUTINE_START(JOB); + + + ASSERT(OriginalNtOpenJobObject); + + rc = OriginalNtOpenJobObject(JobHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(JOB); +} + + + +/* + * InitJobHooks() + * + * Description: + * Initializes all the mediated job object operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitJobHooks() +{ + if ( (OriginalNtCreateJobObject = (fpZwCreateJobObject) ZwCalls[ZW_CREATE_JOBOBJECT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_JOB, LOG_PRIORITY_DEBUG, ("InitJobObjectHooks: OriginalNtCreateJobObject is NULL\n")); + return FALSE; + } + + if ( (OriginalNtOpenJobObject = (fpZwOpenJobObject) ZwCalls[ZW_OPEN_JOBOBJECT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_JOB, LOG_PRIORITY_DEBUG, ("InitJobObjectHooks: OriginalNtOpenJobObject is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/job.h b/job.h new file mode 100644 index 0000000..8328d42 --- /dev/null +++ b/job.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * job.h + * + * Abstract: + * + * This module defines various types used by job object hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __JOB_H__ +#define __JOB_H__ + + + +/* + * ZwCreateJobObject creates or opens a job object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateJobObject) ( + OUT PHANDLE JobHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS +NTAPI +HookedNtCreateJobObject( + OUT PHANDLE JobHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +/* + * ZwOpenJobObject opens a job object. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenJobObject) ( + OUT PHANDLE JobHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS +NTAPI +HookedNtOpenJobObject( + OUT PHANDLE JobHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +BOOLEAN InitJobHooks(); + + +#endif /* __JOB_H__ */ diff --git a/learn.c b/learn.c new file mode 100644 index 0000000..0bcdd53 --- /dev/null +++ b/learn.c @@ -0,0 +1,1414 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * learn.c + * + * Abstract: + * + * This module implements various policy-auto-generation routines. + * Policy auto generation works by monitoring all system calls and remembering their + * argument values. These are then saved in a policy file. + * + * Author: + * + * Eugene Tsyrklevich 24-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "learn.h" +#include "accessmask.h" +#include "hookproc.h" +#include "procname.h" +#include "process.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitLearningMode) +#endif + + +//XXX on a terminal server, global\* might need to be handled specially + +WCHAR ProcessToMonitor[MAX_PROCESS_NAME] = L""; + +BOOLEAN LearningMode = FALSE; +HANDLE hFile; +INT64 offset; + +BOOLEAN IsGuiThread = FALSE; /* does the process we are profiling contain any GUI threads? */ + +SECURITY_POLICY NewPolicy; + + + +/* + * InitLearningMode() + * + * Description: + * Initialize a learning/training mode. Training mode is used to create process policies that + * describe all the resources used by a process. + * + * Called during driver initialization (DriverEntry()) or from DriverDeviceControl(). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitLearningMode() +{ + LOG(LOG_SS_LEARN, LOG_PRIORITY_VERBOSE, ("InitLearningMode: Entered.\n")); + + + /* initialize the NewPolicy */ + RtlZeroMemory(&NewPolicy, sizeof(NewPolicy)); + + KeInitializeSpinLock(&NewPolicy.SpinLock); + + NewPolicy.ProtectionFlags = PROTECTION_ALL_ON; + + + /* + * Load an existing policy (if there is one) and use it as a template + */ + + if (FindAndLoadSecurityPolicy(&NewPolicy, ProcessToMonitor, NULL) == FALSE) + { + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("InitLearningMode: Learning about '%S'. An existing policy not found.\n", ProcessToMonitor)); + + NewPolicy.DefaultPolicyAction = ACTION_LOG; + } + else + { + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("InitLearningMode: Learning about '%S'. Using a pre-existing policy.\n", ProcessToMonitor)); + } + + + return TRUE; +} + + + +/************************************************************************************************* + * + * Path Filters + * Used to convert kernel object names into their userland representation + * (i.e. registry \REGISTRY\USER will be converted to HKEY_USERS + * + *************************************************************************************************/ + + + +/* + * FilterFilePath() + * + * Description: + * Convert kernel file paths to their userland representation (i.e. removing \??\, \DosDevices\, references). + * + * Parameters: + * path - Pointer to a source character file path. + * + * Returns: + * Pointer to a post-processed file path. + */ + +PCHAR +FilterFilePath(PCHAR path) +{ + CHAR buffer[MAX_PATH]; + + + if (path[0] != '\\' && path[0] != '%') + { +// KdPrint(("FilterFilePath: special filename %s\n", path)); +// return NULL; + } + + + if (_strnicmp(path, "\\??\\pipe", 8) == 0) + { + return path + 3; + } + + if (_strnicmp(path, "\\??\\", 4) == 0) + { + path += 4; + } + else if (_strnicmp(path, "\\DosDevices\\", 12) == 0) + { + path += 12; + } + else if (_strnicmp(path, CDrive, CDriveLength) == 0 && CDriveLength) + { + /* replace any possible \device\harddiskvolumeX references with a DOS C:\ name */ + //XXX what about d:\ and so on? + + path[CDriveLength-2] = 'C'; + path[CDriveLength-1] = ':'; + + path += CDriveLength - 2; + } + + + + if (_strnicmp(path, SystemRootDirectory, SystemRootDirectoryLength) == 0 && SystemRootDirectoryLength) + { + /* replace \windows (systemroot) references with SystemRoot */ + + strcpy(buffer, "%SystemRoot%"); + strcat(buffer, path + SystemRootDirectoryLength); + + path = buffer; + } + else if (_strnicmp(path, SystemRootUnresolved, SystemRootUnresolvedLength) == 0 && SystemRootUnresolvedLength) + { + /* replace c:\windows (systemroot) references with SystemRoot */ + + strcpy(buffer, "%SystemRoot%"); + strcat(buffer, path + SystemRootUnresolvedLength); + + path = buffer; + } + else if (_strnicmp(path, "\\SystemRoot\\", 12) == 0) + { + strcpy(buffer, "%SystemRoot%"); + strcat(buffer, path + 11); + + path = buffer; + } + else if ((path[0] == SystemDrive || path[0] == (CHAR) tolower(SystemDrive)) && path[1] == ':') + { + /* replace any system drive references with "SystemDrive" */ + strcpy(buffer, "%SystemDrive%"); + strcat(buffer, path + 1); + + path = buffer; + } + + + return path; +} + + + +/* + * FilterMailslotPath() + * + * Description: + * Convert kernel mailslot paths to their userland representation. + * + * Parameters: + * path - Pointer to a source character path. + * + * Returns: + * Pointer to a post-processed path. + */ + +PCHAR +FilterMailslotPath(PCHAR path) +{ + if (_strnicmp(path, "\\mailslot\\", 10) == 0) + return path + 10; + + if (_strnicmp(path, "\\??\\mailslot\\", 13) == 0) + return path + 13; + + if (_strnicmp(path, "\\device\\mailslot\\", 17) == 0) + return path + 17; + + return path; +} + + + +/* + * FilterNamedpipePath() + * + * Description: + * Convert kernel namedpipe paths to their userland representation. + * + * Parameters: + * path - Pointer to a source character path. + * + * Returns: + * Pointer to a post-processed path. + */ + +PCHAR +FilterNamedpipePath(PCHAR path) +{ + if (_strnicmp(path, "\\pipe\\", 6) == 0) + return path + 6; + + if (_strnicmp(path, "\\??\\pipe\\", 9) == 0) + return path + 9; + + if (_strnicmp(path, "\\device\\namedpipe\\", 18) == 0) + return path + 18; + + return path; +} + + + +/* + * FilterRegistryPath() + * + * Description: + * Convert kernel registry paths to their userland equivalents + * (i.e. replacing \REGISTRY\USER\ kernel path with its userland HKEY_USERS\ representation). + * + * Parameters: + * path - Pointer to a source character registry path. + * + * Returns: + * Pointer to a post-processed registry path. + */ + +PCHAR +FilterRegistryPath(PCHAR path) +{ + static char buffer[MAX_PATH]; + + +//XXX use reverse symlink lookup for this?!?!? + + if (_strnicmp(path, "\\REGISTRY\\", 10) == 0) + { + /* replace \Registry\User\ with HKEY_USERS\ */ + if (_strnicmp(path + 10, "USER\\", 5) == 0) + { + strcpy(path + 4, "HKEY_USERS"); + path[14] = '\\'; + return path + 4; + } + + /* replace \Registry\Machine\ with HKEY_LOCAL_MACHINE\ */ + if (_strnicmp(path + 10, "MACHINE\\", 8) == 0) + { + strcpy(buffer, "HKEY_LOCAL_MACHINE\\"); + strncat(buffer, path + 18, MAX_PATH - 20); + + return buffer; + } + } + + + return path; +} + + + +/* + * FilterBaseNamedObjectsPath() + * + * Description: + * Convert kernel paths to their userland representation (by removing \BaseNamedObjects\ kernel reference). + * + * Parameters: + * path - Pointer to a source character event or semaphore path. + * + * Returns: + * Pointer to a post-processed path. + */ + +PCHAR +FilterBaseNamedObjectsPath(PCHAR path) +{ + if (_strnicmp(path, "\\BaseNamedObjects\\", 18) == 0) + { + return path + 18; + } + + return path; +} + + + +/* + * FilterDll() + * + * Description: + * Convert kernel DLL path to its userland representation + * (i.e. removing \KnownDlls\ kernel reference). + * + * Parameters: + * path - Pointer to a source character DLL path. + * + * Returns: + * Pointer to a post-processed DLL path. + */ + +PCHAR +FilterDll(PCHAR path) +{ + if (_strnicmp(path, "\\KnownDlls\\", 11) == 0) + { + return path + 11; + } + + return path; +} + + + +/* + * FilterPath() + * + * Description: + * Filter stub. + * + * Parameters: + * path - Pointer to a source character path. + * + * Returns: + * Pointer to an unmodified path. + */ + +PCHAR +FilterPath(PCHAR path) +{ + return path; +} + + + +/************************************************************************************************* + * + * Operation Filters + * Convert operations (such as OP_READ, OP_WRITE) into their ASCII representation. + * (i.e. file OP_READ will be translated to "read" while atom OP_READ will become "find" + * + *************************************************************************************************/ + + + +/* + * FilterOperation() + * + * Description: + * Convert operations (such as OP_READ, OP_WRITE) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_READ_WRITE) && + (IS_BIT_SET(OperationType, OP_DELETE) || IS_BIT_SET(OperationType, OP_EXECUTE))) + + return "all"; + + + if (IS_BIT_SET(OperationType, OP_DELETE)) + return "delete"; + + if (IS_BIT_SET(OperationType, OP_READ_WRITE)) + return "rw"; + + if (IS_BIT_SET(OperationType, OP_READ)) + return "read"; + + if (IS_BIT_SET(OperationType, OP_WRITE)) + return "write"; + + if (IS_BIT_SET(OperationType, OP_EXECUTE)) + return "execute"; + + + //XXX what about when multiple bits are set? read + delete? + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("FilterOperation: invalid operation type %d\n", OperationType)); + + + return "all"; +} + + + +/* + * FilterSimpleOperation() + * + * Description: + * Convert operations (such as OP_READ, OP_WRITE) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterSimpleOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_READ_WRITE)) + return "all"; + + if (IS_BIT_SET(OperationType, OP_READ)) + return "read"; + + if (IS_BIT_SET(OperationType, OP_WRITE)) + return "write"; + + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("FilterSimpleOperation: invalid operation type %d\n", OperationType)); + + + return "all"; +} + + + +/* + * FilterCreateOpenOperation() + * + * Description: + * Convert OP_CREATE and OP_OPEN) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterCreateOpenOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_CREATE) && + IS_BIT_SET(OperationType, OP_OPEN)) + + return "all"; + + if (IS_BIT_SET(OperationType, OP_CREATE)) + return "create"; + + if (IS_BIT_SET(OperationType, OP_OPEN)) + return "open"; + + + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("FilterCreateOpenOperation: invalid operation type %d\n", OperationType)); + + + return "all"; +} + + + +/* + * FilterDirectoryOperation() + * + * Description: + * Convert operations (such as OP_DIR_CREATE) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterDirectoryOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_DIR_CREATE)) + return "create"; + + return "all"; +} + + + +/* + * FilterProcessOperation() + * + * Description: + * Convert operations (such as OP_PROC_OPEN) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterProcessOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_PROC_OPEN) && + IS_BIT_SET(OperationType, OP_PROC_EXECUTE)) + + return "all"; + + + if (IS_BIT_SET(OperationType, OP_PROC_OPEN)) + return "open"; + + if (IS_BIT_SET(OperationType, OP_PROC_EXECUTE)) + return "execute"; + + + return "all"; +} + + + +/* + * FilterNetworkOperation() + * + * Description: + * Convert operations (such as OP_CONNECT) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterNetworkOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_TCPCONNECT)) + return "tcpconnect"; + + if (IS_BIT_SET(OperationType, OP_UDPCONNECT)) + return "udpconnect"; + + if (IS_BIT_SET(OperationType, OP_CONNECT)) + return "connect"; + + if (IS_BIT_SET(OperationType, OP_BIND)) + return "bind"; + + + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("FilterNetworkOperation: invalid operation type %d\n", OperationType)); + + + return "all"; +} + + + +/* + * FilterPortOperation() + * + * Description: + * Convert port operations (such as OP_PORT_CONNECT) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterPortOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_PORT_CREATE) && + IS_BIT_SET(OperationType, OP_PORT_CONNECT)) + + return "all"; + + + if (IS_BIT_SET(OperationType, OP_PORT_CREATE)) + return "create"; + + if (IS_BIT_SET(OperationType, OP_PORT_CONNECT)) + return "connect"; + + + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("FilterPortOperation: invalid operation type %d\n", OperationType)); + + + return "all"; +} + + + +/* + * FilterAtomOperation() + * + * Description: + * Convert atom operations (such as OP_READ, OP_WRITE) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterAtomOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_ADD) && + IS_BIT_SET(OperationType, OP_FIND)) + + return "all"; + + if (IS_BIT_SET(OperationType, OP_ADD)) + return "add"; + + if (IS_BIT_SET(OperationType, OP_FIND)) + return "find"; + + + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("FilterAtomOperation: invalid operation type %d\n", OperationType)); + + + return "all"; +} + + + +/* + * FilterDriverOperation() + * + * Description: + * Convert driver object operations (such as OP_READ, OP_WRITE) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterDriverOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_LOAD)) + return "load"; + + if (IS_BIT_SET(OperationType, OP_REGLOAD)) + return "regload"; + + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("FilterDriverOperation: invalid operation type %d\n", OperationType)); + + return "load"; +} + + + +/* + * FilterDllOperation() + * + * Description: + * Convert DLL operations (such as OP_READ, OP_WRITE) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterDllOperation(UCHAR OperationType) +{ + return "load"; +} + + + +/* + * FilterServiceOperation() + * + * Description: + * Convert service operations (such as OP_START, OP_WRITE) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterServiceOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_SERVICE_START)) + return "start"; + + if (IS_BIT_SET(OperationType, OP_SERVICE_STOP)) + return "stop"; + + if (IS_BIT_SET(OperationType, OP_SERVICE_CREATE)) + return "create"; + + if (IS_BIT_SET(OperationType, OP_SERVICE_DELETE)) + return "delete"; + + + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("FilterServiceOperation: invalid operation type %d\n", OperationType)); + + + return "all"; +} + + + +/* + * FilterTimeOperation() + * + * Description: + * Convert time operations (such as OP_TIME_CHANGE) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterTimeOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_TIME_CHANGE)) + return "change"; + + + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("FilterTimeOperation: invalid operation type %d\n", OperationType)); + + + return "all"; +} + + + +/* + * FilterTokenOperation() + * + * Description: + * Convert token operations (such as OP_TOKEN_MODIFY) into their ASCII representation. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * Pointer to an ASCII string. + */ + +PCHAR +FilterTokenOperation(UCHAR OperationType) +{ + if (IS_BIT_SET(OperationType, OP_TOKEN_MODIFY)) + return "modify"; + + + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("FilterTokenOperation: invalid operation type %d\n", OperationType)); + + + return "all"; +} + + + +/* + * FilterSyscallOperation() + * + * Description: + * This function is never supposed to be called. + * + * Parameters: + * OperationType - operation type. + * + * Returns: + * An error. + */ + +PCHAR +FilterSyscallOperation(UCHAR OperationType) +{ + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("FilterSyscallOperation: this function is not supposed to be called.\n")); + + return "error"; +} + + + +typedef PCHAR (*fpFilterObject) (PCHAR path); +typedef PCHAR (*fpFilterOperation) (UCHAR OperationType); + + +/* in C++ these would be member methods */ + +struct +{ + PCHAR Prefix; + fpFilterObject FilterObjectProc; + fpFilterOperation FilterOperationProc; + +} RuleTypeData[] = +{ + { "file", FilterFilePath, FilterOperation }, + { "directory", FilterFilePath, FilterDirectoryOperation }, + { "mailslot", FilterMailslotPath, FilterSimpleOperation }, + { "namedpipe", FilterNamedpipePath, FilterSimpleOperation }, + { "registry", FilterRegistryPath, FilterOperation }, + { "section", FilterBaseNamedObjectsPath, FilterOperation }, + { "dll", FilterDll, FilterDllOperation }, + { "event", FilterBaseNamedObjectsPath, FilterCreateOpenOperation }, + { "semaphore", FilterBaseNamedObjectsPath, FilterCreateOpenOperation }, + { "job", FilterBaseNamedObjectsPath, FilterCreateOpenOperation }, + { "mutex", FilterBaseNamedObjectsPath, FilterCreateOpenOperation }, + { "port", FilterPath, FilterPortOperation }, + { "symlink", FilterPath, FilterCreateOpenOperation }, + { "timer", FilterBaseNamedObjectsPath, FilterCreateOpenOperation }, + { "process", FilterFilePath, FilterProcessOperation }, + { "driver", FilterFilePath, FilterDriverOperation }, + { "dirobj", FilterPath, FilterCreateOpenOperation }, + { "atom", FilterPath, FilterAtomOperation }, + + { "network", FilterPath, FilterNetworkOperation }, + { "service", FilterPath, FilterServiceOperation }, + { "time", FilterPath, FilterTimeOperation }, + { "token", FilterPath, FilterTokenOperation }, + { "syscall", FilterPath, FilterSyscallOperation }, +}; + + + +/* + * DecodeAction() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * . + */ + +PCHAR +DecodeAction(UCHAR ActionType) +{ + switch (ActionType) + { + case ACTION_ASK: + case ACTION_ASK_DEFAULT: + return "ask"; + + case ACTION_PERMIT: + case ACTION_ASK_PERMIT: + case ACTION_PERMIT_DEFAULT: + return "permit"; + + case ACTION_DENY: + case ACTION_ASK_DENY: + case ACTION_DENY_DEFAULT: + return "deny"; + + case ACTION_LOG: + case ACTION_ASK_LOG: + case ACTION_LOG_DEFAULT: + return "log"; + + case ACTION_QUIETDENY: + case ACTION_QUIETDENY_DEFAULT: + return "quietdeny"; + + case ACTION_TERMINATE: + case ACTION_ASK_TERMINATE: + return "log"; + + default: + return "unknown"; + } +} + + + +/* + * CreateRule() + * + * Description: + * Generate an ASCII rule for rule 'r' of type RuleType. + * + * Parameters: + * RuleType - rule type. + * r - rule itself. + * buffer - output buffer. + * + * Returns: + * TRUE if a rule was created, FALSE otherwise. + */ + +BOOLEAN +CreateRule(RULE_TYPE RuleType, PPOLICY_RULE r, PCHAR buffer) +{ + PCHAR name; + + + ASSERT(r); + ASSERT(buffer); + + + if (r->MatchType != MATCH_ALL) + { + name = RuleTypeData[ RuleType ].FilterObjectProc(r->Name); + + /* if name is NULL there is no need to remember this rule */ + if (name == NULL) + return FALSE; + + if (strlen(name) > MAX_PATH) + { + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("CreateRule: name '%s' is too long\n", name)); + return FALSE; + } + + } + + + /* + * construct the "objecttype_operation: " part first + */ + + strcpy(buffer, RuleTypeData[ RuleType ].Prefix); + + strcat(buffer, "_"); + + strcat(buffer, RuleTypeData[ RuleType ].FilterOperationProc(r->OperationType)); + + strcat(buffer, ": "); + + + /* + * now construct the action part + */ + + if (r->MatchType != MATCH_ALL) + { + strcat(buffer, "name "); + + if (r->MatchType == MATCH_WILDCARD) + strcat(buffer, "match \""); + else + strcat(buffer, "eq \""); + + strcat(buffer, name); + + strcat(buffer, "\" then "); + } + + strcat(buffer, DecodeAction(r->ActionType)); + + + /* add optional rule clause */ + if (r->RuleNumber != 0) + { + CHAR rule[16]; + + sprintf(rule, " [rule %d]", r->RuleNumber); + + strcat(buffer, rule); + } + + + return TRUE; +} + + + +/* + * CreateServiceRule() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * . + */ + +BOOLEAN +CreateServiceRule(PPOLICY_RULE r, PCHAR buffer) +{ + strcpy(buffer, "service_"); + strcat(buffer, r->Name + 2); + strcat(buffer, ": permit"); + + return TRUE; +} + + + +BOOLEAN WriteRule(PCHAR rule); +BOOLEAN WritePolicyFile(PCHAR buffer); + + +/* + * FlushPolicy() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * Nothing. + */ + +void +FlushPolicy() +{ + PPOLICY_RULE r; + KIRQL irql; + char buffer[MAX_PATH*2]; + UCHAR i; + BOOLEAN ValidRule; + + + // cannot write to files while holding spinlock (irql != PASSIVE_LEVEL) +// KeAcquireSpinLock(&NewPolicy.SpinLock, &irql); + + sprintf(buffer, "\r\n# %S Default Process Policy\r\n", ProcessToMonitor); + WriteRule(buffer); + + sprintf(buffer, "policy_default: %s\r\n", DecodeAction(NewPolicy.DefaultPolicyAction)); + WriteRule(buffer); + + if (! IS_OVERFLOW_PROTECTION_ON(NewPolicy)) + WriteRule("protection_overflow: off"); + + if (! IS_USERLAND_PROTECTION_ON(NewPolicy)) + WriteRule("protection_userland: off"); + + if (! IS_EXTENSION_PROTECTION_ON(NewPolicy)) + WriteRule("protection_extension: off"); + + if (! IS_DEBUGGING_PROTECTION_ON(NewPolicy)) + WriteRule("protection_debugging: off"); + + if (! IS_VDM_PROTECTION_ON(NewPolicy)) + WriteRule("protection_dos16: off"); + + +// KdPrint(("%s process\n", IsGuiThread ? "GUI" : "NON GUI")); + + + for (i = 0; i < RULE_LASTONE; i++) + { + r = NewPolicy.RuleList[i]; + + if (r) + { + WritePolicyFile("\r\n\r\n# "); + WritePolicyFile(RuleTypeData[i].Prefix); + WritePolicyFile(" related operations\r\n\r\n"); + } + + while (r) + { + if (i != RULE_SYSCALL) + ValidRule = CreateRule(i, r, buffer); + else + ValidRule = CreateServiceRule(r, buffer); + + if (ValidRule == TRUE) + WriteRule(buffer); + + r = (PPOLICY_RULE) r->Next; + } + } + + +// KeReleaseSpinLock(&NewPolicy.SpinLock, irql); +} + + + +/* + * ShutdownLearningMode() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * Nothing. + */ + +BOOLEAN +ShutdownLearningMode() +{ + UNICODE_STRING pathname; + OBJECT_ATTRIBUTES oa; + IO_STATUS_BLOCK isb; + WCHAR PolicyPath[MAX_PATH]; + + + /* now open a file where the new policy will be written, possibly clobbering the old policy */ + //XXX should really copy an existing policy to a .bak file + +// _snwprintf(PolicyPath, MAX_PATH, L"\\??\\c:\\policy\\%s.policy", ProcessToMonitor); + _snwprintf(PolicyPath, MAX_PATH, L"\\??\\%s\\policy\\%s.policy", OzoneInstallPath, ProcessToMonitor); + PolicyPath[MAX_PATH - 1] = 0; + + + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("ShutdownLearningMode: Writing policy to %S\n", PolicyPath)); + + + RtlInitUnicodeString(&pathname, PolicyPath); + + InitializeObjectAttributes(&oa, &pathname, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + if (!NT_SUCCESS(ZwCreateFile(&hFile, GENERIC_WRITE, &oa, &isb, + NULL, 0, 0, FILE_SUPERSEDE, + FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0))) + { + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("ShutdownLearningMode: Failed to open file %S\n", pathname.Buffer)); + return FALSE; + } + + offset = 0; + + FlushPolicy(); + + PolicyDelete(&NewPolicy); + + ZwClose(hFile); + hFile = 0; + + + return TRUE; +} + + + +/* + * WritePolicyFile() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * Nothing. + */ + +BOOLEAN +WritePolicyFile(PCHAR buffer) +{ + int len = strlen(buffer); + IO_STATUS_BLOCK isb; + + + if (!NT_SUCCESS(ZwWriteFile(hFile, NULL, NULL, NULL, &isb, (PVOID) buffer, len, (PLARGE_INTEGER) &offset, NULL))) + { + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("WritePolicyFile(): ZwReadFile failed\n")); + return FALSE; + } + + if (isb.Information != len) + { + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("WritePolicyFile(): Asked to write %d bytes. Wrote only %d\n", len, isb.Information)); + } + + offset += isb.Information; + + + return TRUE; +} + + + +/* + * WriteRule() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * Nothing. + */ + +BOOLEAN +WriteRule(PCHAR rule) +{ + BOOLEAN ret, ret2; + + ret = WritePolicyFile(rule); + ret2 = WritePolicyFile("\r\n"); + + return ret && ret2; +} + + + +/* + * RememberRule() + * + * Description: + * Create a new rule. + * + * Parameters: + * RuleType - rule type. + * ObjectName - name of an object associated with the current rule. + * OperationType - operation type. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +RememberRule(RULE_TYPE RuleType, PCHAR ObjectName, UCHAR OperationType) +{ + PPOLICY_RULE rule = NewPolicy.RuleList[RuleType]; + KIRQL irql; + int len = 0; + + + if (ObjectName) + len = strlen(ObjectName); + + + KeAcquireSpinLock(&NewPolicy.SpinLock, &irql); + + + /* + * don't save duplicate rules + */ + + while (rule != NULL) + { + if ( (rule->MatchType == MATCH_ALL) || + (rule->MatchType == MATCH_SINGLE && len == rule->NameLength && _stricmp(ObjectName, rule->Name) == 0) || + (rule->MatchType == MATCH_WILDCARD && WildcardMatch(ObjectName, rule->Name) == WILDCARD_MATCH) ) + { + rule->OperationType |= OperationType; + + KeReleaseSpinLock(&NewPolicy.SpinLock, irql); + + return TRUE; + } + + rule = (PPOLICY_RULE) rule->Next; + } + + + rule = ExAllocatePoolWithTag(NonPagedPool, sizeof(POLICY_RULE) + len, _POOL_TAG); + if (rule == NULL) + { + LOG(LOG_SS_LEARN, LOG_PRIORITY_DEBUG, ("RememberRule: out of memory\n")); + return FALSE; + } + + RtlZeroMemory(rule, sizeof(POLICY_RULE)); + + rule->ActionType = ACTION_PERMIT; + rule->OperationType = OperationType; + + if (ObjectName) + { + rule->NameLength = (USHORT) len; + strcpy(rule->Name, ObjectName); + rule->MatchType = MATCH_SINGLE; + } + else + { + rule->MatchType = MATCH_ALL; + } + + rule->Next = NewPolicy.RuleList[RuleType]; + NewPolicy.RuleList[RuleType] = rule; + + + KeReleaseSpinLock(&NewPolicy.SpinLock, irql); + + + return TRUE; +} + + + +/* + * DetermineThreadType() + * + * Description: + * Determine whether a thread is GUI enabled or not. Done by checking which + * system call table the thread is using. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +void +DetermineThreadType() +{ + if (ThreadServiceTableOffset == 0) + return; + + if (* (PULONG) ((PCHAR) PsGetCurrentThread() + ThreadServiceTableOffset) != (ULONG) &KeServiceDescriptorTable[0]) + IsGuiThread = TRUE; +} + + + +/* + * AddRule() + * + * Description: + * Create a new rule for a particular rule type, operation type and object. + * + * Parameters: + * RuleType - rule type. + * str - optional character object name. + * OperationType - operation taking place. + * + * Returns: + * Nothing. + */ + +BOOLEAN +AddRule(RULE_TYPE RuleType, PCHAR str, UCHAR OperationType) +{ + PWSTR filename; + + + filename = wcsrchr(GetCurrentProcessName(), L'\\'); + if (filename == NULL) + filename = GetCurrentProcessName(); + else + ++filename; + + if (_wcsicmp(filename, ProcessToMonitor) != 0) + return TRUE; + +// DetermineThreadType(); + + return RememberRule(RuleType, str, OperationType); +} diff --git a/learn.h b/learn.h new file mode 100644 index 0000000..e8a3866 --- /dev/null +++ b/learn.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * learn.h + * + * Abstract: + * + * This module implements various types and definitions used by the policy-auto-generation code. + * + * Author: + * + * Eugene Tsyrklevich 24-Feb-2004 + * + * Revision History: + * + * None. + */ + +#ifndef __LEARN_H__ +#define __LEARN_H__ + + +#include "policy.h" + + +extern BOOLEAN LearningMode; + +extern SECURITY_POLICY NewPolicy; + +/* In characters */ +#define MAX_PROCESS_NAME 32 + +extern WCHAR ProcessToMonitor[]; + + +BOOLEAN InitLearningMode(); +BOOLEAN ShutdownLearningMode(); +BOOLEAN AddRule(RULE_TYPE RuleType, PCHAR str, UCHAR OperationType); + + +#endif /* __LEARN_H__ */ \ No newline at end of file diff --git a/log.c b/log.c new file mode 100644 index 0000000..7048b05 --- /dev/null +++ b/log.c @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * log.c + * + * Abstract: + * + * This module implements various alert logging routines. + * + * Author: + * + * Eugene Tsyrklevich 24-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include "log.h" +#include "procname.h" +#include "policy.h" // CDrive declaration + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitLog) +#pragma alloc_text (PAGE, ShutdownLog) +#pragma alloc_text (PAGE, LogPostBootup) +#endif + + +KSPIN_LOCK gLogSpinLock; + +PKEVENT LogUserEvent = NULL; +HANDLE LogUserEventHandle = NULL; + +PSECURITY_ALERT LogList = NULL; /* alert queue */ +PSECURITY_ALERT LastAlert = NULL; /* pointer to the last queued alert, used for quick inserts */ + +USHORT NumberOfAlerts; /* number of queued alerts */ + + + +/* + * GetObjectAccessAlertPriority() + * + * Description: + * Figure out a priority for object access alert category. + * + * Parameters: + * AlertSubSystem + * Operation + * ActionTaken + * + * Returns: + * Chosen priority. + */ + +ALERT_PRIORITY +GetObjectAccessAlertPriority(UCHAR AlertSubSystem, UCHAR Operation, ACTION_TYPE ActionTaken) +{ + switch (AlertSubSystem) + { + case RULE_FILE: + case RULE_DIRECTORY: + case RULE_NAMEDPIPE: + case RULE_REGISTRY: + case RULE_SECTION: + case RULE_JOB: + case RULE_PORT: + case RULE_SYMLINK: + + /* default actions are given low priority */ + if (ActionTaken & ACTION_DEFAULT) + return ALERT_PRIORITY_LOW; + + /* non-read operations are marked medium priority */ + if (Operation != OP_READ) + return ALERT_PRIORITY_MEDIUM; + + /* whilst read operations are marked low priority */ + return ALERT_PRIORITY_LOW; + + + /* high priority rules */ + case RULE_DLL: + case RULE_PROCESS: + case RULE_DRIVER: + case RULE_NETWORK: + case RULE_SERVICE: + + return ALERT_PRIORITY_HIGH; + + + case RULE_TIME: + case RULE_TOKEN: + + return ALERT_PRIORITY_MEDIUM; + + + /* low priority rules */ + case RULE_MAILSLOT: + case RULE_EVENT: + case RULE_SEMAPHORE: + case RULE_MUTANT: + case RULE_DIROBJ: + case RULE_ATOM: + case RULE_SYSCALL: + case RULE_TIMER: + + return ALERT_PRIORITY_LOW; + + + default: + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("GetAlertPriority: Unknown alert subsystem %d\n", AlertSubSystem)); + return ALERT_PRIORITY_MEDIUM; + } +} + + + +/* + * FilterObjectName() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * Nothing. + */ +//XXX move elsewhere +PCHAR +FilterObjectName(PCHAR ObjectName) +{ + if (_strnicmp(ObjectName, "\\??\\PIPE\\", 9) == 0) + return ObjectName + 3; + + if (_strnicmp(ObjectName, "\\??\\", 4) == 0) + return ObjectName + 4; + + if (_strnicmp(ObjectName, "\\DosDevices\\", 12) == 0) + return ObjectName + 12; + + if (_strnicmp(ObjectName, "\\BaseNamedObjects\\", 18) == 0) + return ObjectName + 18; + + if (_strnicmp(ObjectName, CDrive, CDriveLength) == 0 && CDriveLength) + { + /* replace any possible \device\harddiskvolumeX references with a DOS C:\ name */ + ObjectName[CDriveLength-2] = 'C'; + ObjectName[CDriveLength-1] = ':'; + + ObjectName += CDriveLength - 2; + } + + + return ObjectName; +} + + + +/* + * LogAlert() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * Nothing. + */ + +VOID +LogAlert(UCHAR AlertSubSystem, UCHAR OperationType, UCHAR AlertRuleNumber, ACTION_TYPE ActionTaken, ALERT_PRIORITY AlertPriority, PWSTR PolicyFilename, USHORT PolicyLineNumber, PCHAR ObjectName) +{ + USHORT Size; + PSECURITY_ALERT pAlert; + KIRQL irql; + +#define NAME_BUFFER_SIZE 256 + ANSI_STRING asObjectName; + UNICODE_STRING usObjectName; + WCHAR ProcessName[NAME_BUFFER_SIZE], ObjectNameW[NAME_BUFFER_SIZE] = { 0 }; + USHORT ObjectNameLength = 0, ProcessNameLength = 0, PolicyNameLength = 0; + +/* + if (PolicyFilename == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, (("LogAlert: NULL PolicyFilename\n")); + return; + } +*/ + + /* \KnownDlls section rules need to be converted to DLLs */ + if (AlertSubSystem == RULE_SECTION) + { + if (_strnicmp(ObjectName, "\\KnownDlls\\", 11) == 0) + { + ObjectName += 11; + AlertSubSystem = RULE_DLL; + OperationType = OP_LOAD; + } + } + + + if (ObjectName) + { + ObjectName = FilterObjectName(ObjectName); + + usObjectName.Length = 0; + usObjectName.MaximumLength = sizeof(ObjectNameW); + usObjectName.Buffer = ObjectNameW; + + RtlInitAnsiString(&asObjectName, ObjectName); + + RtlAnsiStringToUnicodeString(&usObjectName, &asObjectName, FALSE); + ObjectNameW[ asObjectName.Length ] = 0; + } + + + wcsncpy(ProcessName, GetCurrentProcessName(), NAME_BUFFER_SIZE - 1); + ProcessName[NAME_BUFFER_SIZE - 1] = 0; + + + /* + * extra +1 for ProcessName & PolicyName zero termination + * (ObjectName zero is covered by SECURITY_ALERT wchar[1] declaration) + */ + + if (ObjectName) + ObjectNameLength = (USHORT) wcslen(ObjectNameW); + + ProcessNameLength = (USHORT) wcslen(ProcessName); + + if (PolicyFilename) + PolicyNameLength = (USHORT) wcslen(PolicyFilename); + + Size = sizeof(SECURITY_ALERT) + (ObjectNameLength + ProcessNameLength + 1 + PolicyNameLength + 1) * sizeof(WCHAR); + + + if ((pAlert = (PSECURITY_ALERT) GetCurrentUserSid(&Size)) == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("NULL UserInfo. Security Alert\n")); + return; + } + + + pAlert->Next = NULL; + pAlert->Size = Size; + pAlert->AlertSubsystem = AlertSubSystem; + pAlert->AlertType = OperationType; + pAlert->AlertRuleNumber = AlertRuleNumber; + pAlert->Priority = AlertPriority; + pAlert->ObjectNameLength = ObjectNameLength; + pAlert->ProcessNameLength = ProcessNameLength; + pAlert->PolicyNameLength = PolicyNameLength; + pAlert->ProcessId = (ULONG) PsGetCurrentProcessId(); + pAlert->Action = ActionTaken; + pAlert->PolicyLineNumber = PolicyLineNumber; + + + if (ObjectName) + wcscpy(pAlert->ObjectName, ObjectNameW); + + /* process name follows the object name */ + wcscpy(pAlert->ObjectName + ObjectNameLength + 1, ProcessName); + + /* policy name follows the process name */ + if (PolicyFilename) + wcscpy(pAlert->ObjectName + ObjectNameLength + 1 + ProcessNameLength + 1, PolicyFilename); + + /* save the alert for user agent to pick-up */ + if (ObjectName) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%S Alert. Name %S. Object type %d. Alert type %d.\n", ProcessName, pAlert->ObjectName, pAlert->AlertSubsystem, pAlert->AlertType)); + } + else + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%S Alert. Object type %d. Alert type %d.\n", ProcessName, pAlert->AlertSubsystem, pAlert->AlertType)); + } + + + KeAcquireSpinLock(&gLogSpinLock, &irql); + { + /* + * Put a ceiling on how many alerts we can queue, otherwise we can run out of memory + * if userland service is down + */ + + if (NumberOfAlerts > MAXIMUM_OUTSTANDING_ALERTS) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("LogAlert: Exceeded maximum number of queued alerts. Dropping.\n")); + + ExFreePoolWithTag(pAlert, _POOL_TAG); + + KeReleaseSpinLock(&gLogSpinLock, irql); + + return; + } + + ++NumberOfAlerts; + + /* + * Append alerts to the end of the list so that they show up in a proper order when + * picked up by the userland service + */ + + if (LogList == NULL) + { + /* first alert on the list */ + LastAlert = LogList = pAlert; + } + else + { + LastAlert->Next = pAlert; + LastAlert = pAlert; + } + +// pAlert->Next = LogList; +// LogList = pAlert; + } + KeReleaseSpinLock(&gLogSpinLock, irql); + + + /* + * Pulse the event to notify the user agent. + * User-mode apps can't reset a kernel mode event, which is why we're pulsing it here. + */ + + if (LogUserEvent) + { + KeSetEvent(LogUserEvent, 0, FALSE); + KeClearEvent(LogUserEvent); + } +} + + + +/* + * LogPostBootup() + * + * Description: + * Initializes logging related data once computer is done booting up. + * + * \BaseNamedObjects object directory is not created until the Win32® system initializes. + * Therefore, drivers that are loaded at boot time cannot create event objects in their DriverEntry + * routines that are visible to user-mode programs. + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +LogPostBootup() +{ + UNICODE_STRING uszLogEventString; + + + ASSERT(BootingUp == FALSE); + + + /* Create events for user-mode processes to monitor */ + RtlInitUnicodeString(&uszLogEventString, LOG_USER_EVENT_NAME); + + + if ((LogUserEvent = IoCreateNotificationEvent(&uszLogEventString, &LogUserEventHandle)) == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("LogPostBootup: IoCreateNotificationEvent failed\n")); + return FALSE; + } + + KeClearEvent(LogUserEvent); + + + return TRUE; +} + + + +/* + * InitLog() + * + * Description: + * Initializes logging related data. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitLog() +{ + KeInitializeSpinLock(&gLogSpinLock); + + + if (BootingUp == FALSE) + + return LogPostBootup(); + + + return TRUE; +} + + + +/* + * ShutdownLog() + * + * Description: + * Clean up the logging subsystem - close all the handles & delete any outstanding alerts. + * + * NOTE: Called once during driver unload (DriverUnload()). + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +VOID +ShutdownLog() +{ + PSECURITY_ALERT tmp; + KIRQL irql; + + + if (LogUserEventHandle) + ZwClose(LogUserEventHandle); + + + /* XXX logs will need to be flushed to a file and then sent to MC */ + KeAcquireSpinLock(&gLogSpinLock, &irql); + { + while (LogList) + { + tmp = LogList; + LogList = LogList->Next; + + ExFreePoolWithTag(tmp, _POOL_TAG); + } + } + KeReleaseSpinLock(&gLogSpinLock, irql); +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..3e5b77e --- /dev/null +++ b/log.h @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * log.h + * + * Abstract: + * + * This module defines various macros used for logging. + * + * Author: + * + * Eugene Tsyrklevich 17-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __LOG_H__ +#define __LOG_H__ + + +#include +#include "ntproto.h" +#include "misc.h" + + +/* maximum number of alerts the kernel will queue */ +#define MAXIMUM_OUTSTANDING_ALERTS 1000 + +#define LOG_USER_EVENT_NAME L"\\BaseNamedObjects\\OzoneLogEvent" + + +/* + * Logging SubSystems + */ + +#define LOG_SS_DRIVER_INTERNAL (1 << 0) +#define LOG_SS_POLICY (1 << 1) +#define LOG_SS_POLICY_PARSER (1 << 2) +#define LOG_SS_FILE (1 << 3) +#define LOG_SS_DIRECTORY (1 << 3) // same as LOG_SS_FILE, used in HookedNtCreateFile +#define LOG_SS_SEMAPHORE (1 << 4) +#define LOG_SS_EVENT (1 << 5) +#define LOG_SS_SECTION (1 << 6) +#define LOG_SS_REGISTRY (1 << 7) +#define LOG_SS_PROCESS (1 << 8) +#define LOG_SS_HOOKPROC (1 << 9) +#define LOG_SS_LEARN (1 << 10) +#define LOG_SS_PATHPROC (1 << 11) +#define LOG_SS_NETWORK (1 << 12) +#define LOG_SS_TIME (1 << 13) +#define LOG_SS_SYSINFO (1 << 14) +#define LOG_SS_JOB (1 << 15) +#define LOG_SS_MUTANT (1 << 16) +#define LOG_SS_PORT (1 << 17) +#define LOG_SS_SYMLINK (1 << 18) +#define LOG_SS_TIMER (1 << 19) +#define LOG_SS_TOKEN (1 << 20) +#define LOG_SS_NAMEDPIPE (1 << 21) +#define LOG_SS_MAILSLOT (1 << 22) +#define LOG_SS_DRIVER (1 << 23) +#define LOG_SS_DIROBJ (1 << 24) +#define LOG_SS_ATOM (1 << 25) +#define LOG_SS_VDM (1 << 26) +#define LOG_SS_DEBUG (1 << 27) +#define LOG_SS_DRIVE (1 << 28) +#define LOG_SS_MISC (1 << 29) + + +/* log the following subsytems */ + +#define LOG_SUBSYSTEMS (LOG_SS_ATOM | \ + LOG_SS_DIROBJ | \ + LOG_SS_DRIVER | \ + LOG_SS_DRIVER_INTERNAL | \ + LOG_SS_EVENT | \ + LOG_SS_FILE | \ + LOG_SS_HOOKPROC | \ + LOG_SS_JOB | \ + LOG_SS_LEARN | \ + LOG_SS_MAILSLOT | \ + LOG_SS_MISC | \ + LOG_SS_MUTANT | \ + LOG_SS_NAMEDPIPE | \ + LOG_SS_NETWORK | \ + LOG_SS_PATHPROC | \ + LOG_SS_POLICY | \ + LOG_SS_POLICY_PARSER | \ + LOG_SS_PORT | \ + LOG_SS_PROCESS | \ + LOG_SS_REGISTRY | \ + LOG_SS_SECTION | \ + LOG_SS_SEMAPHORE | \ + LOG_SS_SYMLINK | \ + LOG_SS_SYSINFO | \ + LOG_SS_TIME | \ + LOG_SS_TIMER | \ + LOG_SS_TOKEN | \ + LOG_SS_VDM | \ + LOG_SS_DRIVE | \ + LOG_SS_DEBUG) + +#define LOG_PRIORITY_VERBOSE 1 +#define LOG_PRIORITY_DEBUG 2 +#define LOG_PRIORITY_WARNING 3 +#define LOG_PRIORITY_ERROR 4 +#define LOG_PRIORITY_CRITICAL 5 + + +#define MINIMUM_LOGGING_PRIORITY LOG_PRIORITY_DEBUG + + +#define LOG(subsystem, priority, msg) \ + do { \ + if (priority >= LOG_PRIORITY_WARNING) { \ + DbgPrint msg; \ + } else if (priority > MINIMUM_LOGGING_PRIORITY){\ + KdPrint(msg); \ + } else if (subsystem & LOG_SUBSYSTEMS) { \ + if (priority == MINIMUM_LOGGING_PRIORITY) { \ + KdPrint(msg); \ + } \ + } \ + } while(0) + + + +/* + * Alert SubSystems (sorted in the same order as RULEs in policy.h) + * + * We have an alert subsystem for each category of alert that Ozone generates. + * (These are mostly the same as RuleTypes plus several extra ones) + */ + +#define ALERT_SS_FILE 0 +#define ALERT_SS_DIRECTORY 1 +#define ALERT_SS_MAILSLOT 2 +#define ALERT_SS_NAMEDPIPE 3 +#define ALERT_SS_REGISTRY 4 +#define ALERT_SS_SECTION 5 +#define ALERT_SS_DLL 6 +#define ALERT_SS_EVENT 7 +#define ALERT_SS_SEMAPHORE 8 +#define ALERT_SS_JOB 9 +#define ALERT_SS_MUTANT 10 +#define ALERT_SS_PORT 11 +#define ALERT_SS_SYMLINK 12 +#define ALERT_SS_TIMER 13 +#define ALERT_SS_PROCESS 14 +#define ALERT_SS_DRIVER 15 +#define ALERT_SS_DIROBJ 16 +#define ALERT_SS_ATOM 17 + +#define ALERT_SS_NETWORK 18 +#define ALERT_SS_SERVICE 19 +#define ALERT_SS_TIME 20 +#define ALERT_SS_TOKEN 21 +#define ALERT_SS_SYSCALL 22 +#define ALERT_SS_VDM 23 +#define ALERT_SS_DEBUG 24 +#define ALERT_SS_BOPROT 25 + + +/* + * Alert Rules + */ + +#define ALERT_RULE_NONE 0 + +#define ALERT_RULE_PROCESS_EXEC_2EXTS 1 // Executing a binary with more than one extension +#define ALERT_RULE_PROCESS_EXEC_UNKNOWN 2 // Executing a binary with an unknown extension +#define ALERT_RULE_PROCESS_EXEC_NOEXT 3 // Executing binary without an extension + +#define ALERT_RULE_BOPROT_INVALIDCALL 1 // System call originating directly from user code + + +/* defined in policy.h */ +typedef unsigned char ACTION_TYPE; +typedef struct _POLICY_RULE POLICY_RULE, *PPOLICY_RULE; +typedef enum _AlertPriority ALERT_PRIORITY; + + +#pragma pack(push, 1) +typedef struct _SECURITY_ALERT +{ + struct _SECURITY_ALERT *Next; + + /* size of the entire alert = sizeof(SECURITY_ALERT) + strlen(ObjectName) + sizeof(SID) */ + USHORT Size; + UCHAR AlertSubsystem; + UCHAR AlertType; + UCHAR AlertRuleNumber; + UCHAR/*ALERT_PRIORITY*/ Priority; + UCHAR/*ACTION_TYPE*/ Action; /* UINT8 Action that was taken (denied, logged) */ + ULONG ProcessId; + USHORT ObjectNameLength; + USHORT ProcessNameLength; + USHORT PolicyNameLength; + USHORT PolicyLineNumber; + + /* space for ObjectName, ProcessName, PolicyName and SID are dynamically allocated */ + WCHAR ObjectName[ANYSIZE_ARRAY]; + + /* ProcessName follows the zero-terminated ObjectName */ +// WCHAR ProcessName[ANYSIZE_ARRAY]; + + /* PolicyName follows the zero-terminated ProcessName */ +// WCHAR PolicyName[ANYSIZE_ARRAY]; + + /* SID follows the zero-terminated PolicyName */ +// SID_AND_ATTRIBUTES UserInfo; + +} SECURITY_ALERT, *PSECURITY_ALERT; +#pragma pack(pop) + + +extern KSPIN_LOCK gLogSpinLock; +extern PSECURITY_ALERT LogList; +extern PSECURITY_ALERT LastAlert; +extern USHORT NumberOfAlerts; + + +BOOLEAN InitLog(); +VOID ShutdownLog(); +VOID LogAlert(UCHAR AlertSubSystem, UCHAR OperationType, UCHAR AlertRuleNumber, ACTION_TYPE ActionTaken, ALERT_PRIORITY AlertPriority, PWSTR PolicyFilename, USHORT PolicyLineNumber, PCHAR ObjectName); +ALERT_PRIORITY GetObjectAccessAlertPriority(UCHAR AlertSubSystem, UCHAR Operation, ACTION_TYPE ActionTaken); +BOOLEAN LogPostBootup(); + +PCHAR FilterObjectName(PCHAR ObjectName); + + +#endif /* __LOG_H__ */ diff --git a/media.c b/media.c new file mode 100644 index 0000000..813bf71 --- /dev/null +++ b/media.c @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * media.c + * + * Abstract: + * + * This module deals with removable media drives. Removable media drives can be made read-only + * or disabled altogether. + * + * Addition of removable drives is detected by monitoring the creation new drive symbolic links. + * Drive removal is detected by receiving Plug-and-Play notification. + * + * Note: Plug-and-Play notifications are not used to detect addition of removable drives since + * drive letters are not yet assigned when notifications arrive. + * + * Author: + * + * Eugene Tsyrklevich 02-Jun-2004 + */ + + +#include + +#undef DEFINE_GUID +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + +#include +#include +#include "media.h" +#include "pathproc.h" +#include "policy.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitRemovableMediaHooks) +#pragma alloc_text (PAGE, RemoveRemovableMediaHooks) +#endif + + +//XXX investigate IoRegisterFsRegistrationChange +//reference: www.pulsetrainsw.com/articlesitem.asp?id=3 + +PVOID DiskNotificationEntry = NULL, CdromNotificationEntry = NULL; + +/* removable media flags defined in drive.h (READONLY, etc) */ +UCHAR MediaRemovableFlags = 0; + + +typedef struct _WORK_CONTEXT +{ + PIO_WORKITEM Item; + UNICODE_STRING SymbolicLinkName; + +} WORK_CONTEXT, *PWORK_CONTEXT; + + + +/* + * AddDrive() + * + * Description: + * Create policy access rules for removable drives as they are added at runtime. + * + * Parameters: + * pusDriveName - . + * DriveLetter - Letter of the drive being added. + * + * Returns: + * Nothing. + */ + +VOID +AddDrive(PUNICODE_STRING pusDriveName, CHAR DriveLetter) +{ + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT fo; + NTSTATUS rc; + + + rc = IoGetDeviceObjectPointer(pusDriveName, FILE_READ_ATTRIBUTES, &fo, &pDeviceObject); + if (! NT_SUCCESS(rc)) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_VERBOSE, ("AddDrive: IoGetDeviceObjectPointer failed with status %x\n", rc)); + return; + } + + + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("AddDrive(%S): %d\n", pusDriveName->Buffer, pDeviceObject->Characteristics)); + + + if (pDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) + { + CHAR rule[MAX_PATH]; + + + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("AddDrive: removable drive! %c: %S\n", DriveLetter, pusDriveName->Buffer)); + + + /* Create a new global policy rule */ + + if (IS_REMOVABLE_MEDIA_DISABLED()) + { + sprintf(rule, "name match \"%c:\\*\" then %s", DriveLetter, "deny"); + + PolicyParseObjectRule(&gSecPolicy, RULE_FILE, "all", rule); + + /* no need to process other rules, this one denies everything already */ + return; + } + + if (IS_REMOVABLE_MEDIA_READONLY() /*&& !(pDeviceObject->Characteristics & FILE_READ_ONLY_DEVICE)*/) + { + sprintf(rule, "name match \"%c:\\*\" then %s", DriveLetter, "deny"); + + PolicyParseObjectRule(&gSecPolicy, RULE_FILE, "write", rule); + } + + if (IS_REMOVABLE_MEDIA_NOEXECUTE()) + { + sprintf(rule, "name match \"%c:\\*\" then %s", DriveLetter, "deny"); + + PolicyParseObjectRule(&gSecPolicy, RULE_FILE, "execute", rule); + } + } + + + return; +} + + + +/* + * RemoveDrive() + * + * Description: + * . + * + * Parameters: + * pusDriveName - . + * + * Returns: + * Nothing. + */ + +VOID +RemoveDrive(PUNICODE_STRING pusDriveName) +{ + PDEVICE_OBJECT pDeviceObject; + PFILE_OBJECT fo; + NTSTATUS rc; + + + rc = IoGetDeviceObjectPointer(pusDriveName, FILE_READ_ATTRIBUTES, &fo, &pDeviceObject); + if (! NT_SUCCESS(rc)) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("RemoveDrive: IoGetDeviceObjectPointer failed with status %x\n", rc)); + return; + } + + + if (pDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("RemoveDrive: removable drive! %S\n", pusDriveName->Buffer)); + + +// rc = RtlVolumeDeviceToDosName(pDeviceObject, pusDriveName); +// if (NT_SUCCESS(rc)) +// LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("RemoveDrive: IoVolumeDeviceToDosName returned %S\n", pusDriveName->Buffer)); +} + + + +/* + * MonitorDriveLinks() + * + * Description: + * This routine is called from HookedNtCreateSymbolicLinkObject whenever a new symbolic link is created. + * ProcessDriveLink is responsible for monitoring the creation of any new media drives. When a new removable + * media drive is added to a system, a new symbolic link "\GLOBAL??\X:" is created (where X is the + * assigned drive letter). + * + * Note: We are not interested in creation of "fake" drives (i.e. subst c: z:\) since those will be + * correctly resolved by symbolic link name resolution code (policy enforcement). "Fake" drives + * are added in userland and come up as "\??\X:". + * + * Parameters: + * Link - created symbolic link name. + * + * Returns: + * Nothing. + */ + +VOID +MonitorDriveLinks(const PCHAR Link) +{ + CHAR DriveLetter; + ANSI_STRING AnsiString; + UNICODE_STRING UnicodeString; + int len = strlen(Link); + + + /* match \GLOBAL??\Z: && \??\Z: drive references */ + if (len == 12 && (strncmp(Link, "\\GLOBAL??\\", 10) == 0) && isalpha(Link[10]) && Link[11] == ':') + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("MonitorDriveLinks: matched \\global??\\. letter %c\n", Link[10])); + + DriveLetter = Link[10]; + } + else if (len == 6 && (strncmp(Link, "\\??\\", 4) == 0) && isalpha(Link[4]) && Link[5] == ':') + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("MonitorDriveLinks: matched \\??\\. letter %c\n", Link[4])); + + DriveLetter = Link[4]; + } + else + { + return; + } + + + RtlInitAnsiString(&AnsiString, Link); + + if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE))) + { + AddDrive(&UnicodeString, DriveLetter); + + RtlFreeUnicodeString(&UnicodeString); + } + else + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("MonitorDriveLinks: failed to convert %s\n", Link)); + } +} + + + +/* + * PnpWorker() + * + * Description: + * A work item routine that runs at PASSIVE_LEVEL irql. The routine is scheduled by PnpCallback + * which is not allowed to block. + * + * PnpWorker calls RemoveDrive() with a drive name setup by PnpCallback. + * + * Parameters: + * pDeviceObject - . + * Context - . + * + * Returns: + * Nothing. + */ + +VOID +PnpWorker(IN PDEVICE_OBJECT pDeviceObject, IN PVOID Context) +{ + PWORK_CONTEXT WorkContext = (PWORK_CONTEXT) Context; + + + if (WorkContext == NULL) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("PnpWorker: WorkContext = NULL\n")); + return; + } + + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("PnpWorker: %S\n", WorkContext->SymbolicLinkName.Buffer)); + + + RemoveDrive(&WorkContext->SymbolicLinkName); + + + IoFreeWorkItem(WorkContext->Item); + + ExFreePoolWithTag(WorkContext, _POOL_TAG); +} + + + +/* + * PnpCallback() + * + * Description: + * Plug-and-Play callback. Gets called when a p-n-p drive/cdrom interface is modified + * (i.e. when a removable drive is added or removed from the system). + * + * Parameters: + * NotificationStructure - DEVICE_INTERFACE_CHANGE_NOTIFICATION indicating which interface changed. + * Context - the driver's device context. + * + * Returns: + * STATUS_SUCCESS. + */ + +NTSTATUS +PnpCallback(IN PVOID NotificationStructure, IN PVOID Context) +{ + PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notify = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION) NotificationStructure; + PDEVICE_OBJECT pDeviceObject = (PDEVICE_OBJECT) Context; + PIO_WORKITEM WorkItem; + PWORK_CONTEXT WorkContext; + + + if (IsEqualGUID((LPGUID) &Notify->Event, (LPGUID) &GUID_DEVICE_INTERFACE_REMOVAL)) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("GUID_DEVICE_INTERFACE_REMOVAL %S\n", Notify->SymbolicLinkName->Buffer)); +/* + } + + + if (IsEqualGUID((LPGUID) &Notify->Event, (LPGUID) &GUID_DEVICE_INTERFACE_ARRIVAL) || + IsEqualGUID((LPGUID) &Notify->Event, (LPGUID) &GUID_DEVICE_INTERFACE_REMOVAL)) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("GUID_DEVICE_INTERFACE_ARRIVAL %x %S\n", Notify->SymbolicLinkName, Notify->SymbolicLinkName->Buffer)); +*/ + + /* + * Schedule a work item to process this request. Cannot block in this callback function. + */ + + WorkItem = IoAllocateWorkItem(pDeviceObject); + + WorkContext = (PWORK_CONTEXT) ExAllocatePoolWithTag(PagedPool, + sizeof(WORK_CONTEXT) + Notify->SymbolicLinkName->Length, + _POOL_TAG); + if (!WorkContext) + { + IoFreeWorkItem(WorkItem); + return(STATUS_SUCCESS); + } + + WorkContext->SymbolicLinkName.Buffer = (PWSTR) ((PCHAR) &WorkContext->SymbolicLinkName + sizeof(UNICODE_STRING)); + WorkContext->SymbolicLinkName.MaximumLength = Notify->SymbolicLinkName->Length; + WorkContext->SymbolicLinkName.Length = 0; + + WorkContext->Item = WorkItem; + RtlCopyUnicodeString(&WorkContext->SymbolicLinkName, Notify->SymbolicLinkName); + + IoQueueWorkItem(WorkItem, PnpWorker, DelayedWorkQueue, WorkContext); + } + + + return STATUS_SUCCESS; +} + + + +/* + * InitRemovableMediaHooks() + * + * Description: + * Process any existing drives and register Plug-and-Play notifications for future drive additions/removals. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitRemovableMediaHooks(IN PDRIVER_OBJECT pDriverObject, IN PDEVICE_OBJECT pDeviceObject) +{ + UNICODE_STRING Name; + WCHAR Buffer[MAX_PATH] = L"\\??\\A:"; + NTSTATUS rc; + HANDLE hLink; + OBJECT_ATTRIBUTES oa; + char DriveLetter; + + + for (DriveLetter = 'A'; DriveLetter <= 'Z'; DriveLetter++) + { + Buffer[4] = DriveLetter; + + RtlInitUnicodeString(&Name, Buffer); + + AddDrive(&Name, DriveLetter); + } + + + rc = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, + 0,//PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (LPGUID) &GUID_DEVINTERFACE_DISK, + pDriverObject, + PnpCallback, + pDeviceObject, + &DiskNotificationEntry); + + if (! NT_SUCCESS(rc)) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("InitRemovableMediaHooks: IoRegisterPlugPlayNotification failed with status %x\n", rc)); + return FALSE; + } + + + rc = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, + 0,//PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (LPGUID) &GUID_DEVINTERFACE_CDROM, + pDriverObject, + PnpCallback, + pDeviceObject, + &CdromNotificationEntry); + + if (! NT_SUCCESS(rc)) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("InitRemovableMediaHooks: IoRegisterPlugPlayNotification2 failed with status %x\n", rc)); + return FALSE; + } + + + return TRUE; +} + + + +/* + * RemoveRemovableMediaHooks() + * + * Description: + * Unregister Plug-and-Play notifications. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +VOID +RemoveRemovableMediaHooks() +{ + if (DiskNotificationEntry) + if (! NT_SUCCESS(IoUnregisterPlugPlayNotification(DiskNotificationEntry))) + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("RemoveRemovableMediaHooks: IoUnregisterPlugPlayNotification failed\n")); + + if (CdromNotificationEntry) + if (! NT_SUCCESS(IoUnregisterPlugPlayNotification(CdromNotificationEntry))) + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("RemoveRemovableMediaHooks: IoUnregisterPlugPlayNotification2 failed\n")); +} diff --git a/media.h b/media.h new file mode 100644 index 0000000..250510e --- /dev/null +++ b/media.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * media.c + * + * Abstract: + * + * This module deals with removable media drives. + * + * Author: + * + * Eugene Tsyrklevich 02-Jun-2004 + */ + + +#ifndef __MEDIA_H__ +#define __MEDIA_H__ + + +#define MEDIA_REMOVABLE_PERMIT 0 +#define MEDIA_REMOVABLE_DISABLE 1 +#define MEDIA_REMOVABLE_READONLY 2 +#define MEDIA_REMOVABLE_NOEXECUTE 4 + +#define IS_REMOVABLE_MEDIA_READONLY() ((MediaRemovableFlags & MEDIA_REMOVABLE_READONLY) == MEDIA_REMOVABLE_READONLY) +#define IS_REMOVABLE_MEDIA_DISABLED() ((MediaRemovableFlags & MEDIA_REMOVABLE_DISABLE) == MEDIA_REMOVABLE_DISABLE) +#define IS_REMOVABLE_MEDIA_NOEXECUTE() ((MediaRemovableFlags & MEDIA_REMOVABLE_NOEXECUTE) == MEDIA_REMOVABLE_NOEXECUTE) + + +extern UCHAR MediaRemovableFlags; + + +BOOLEAN InitRemovableMediaHooks(IN PDRIVER_OBJECT pDriverObject, IN PDEVICE_OBJECT pDeviceObject); +VOID RemoveRemovableMediaHooks(); +VOID MonitorDriveLinks(const PCHAR Link); + + +#endif /* __MEDIA_H__ */ diff --git a/misc.c b/misc.c new file mode 100644 index 0000000..1381272 --- /dev/null +++ b/misc.c @@ -0,0 +1,943 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * misc.c + * + * Abstract: + * + * This module implements various miscellaneous routines. + * + * Author: + * + * Eugene Tsyrklevich 24-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#include "misc.h" +#include "process.h" +#include "userland.h" +#include "policy.h" +#include "token.h" + + +BOOLEAN BootingUp = FALSE; + + +/* + * atoi() + * + * Description: + * Convert a given string to an integer. + * + * Parameters: + * buf - Pointer to a source character string. + * + * Returns: + * String's integer value (0 in case of an error). + */ + +INT32 +atoi(IN PCHAR buf) +{ + int ret = 0; + + + while (*buf >= '0' && *buf <= '9') + { + ret *= 10; + ret += *buf - '0'; + buf++; + } + + + return ret; +} + + + +/* + * itoa() + * + * Description: + * Convert an integer to a string. + * + * Parameters: + * value - Number to be converted. + * string - String result. + * radix - Base of value; must be in the range 2 – 36. + * + * Returns: + * Pointer to a string. + */ + +PCHAR +itoa(int value, char *string, unsigned int radix) +{ + static const char base36[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + PCHAR s; + int tmp = value; + int digits = 1; + + + ASSERT(radix > 1 && radix < (int) sizeof base36); + ASSERT(string); + ASSERT(value >= 0); + + while ((tmp /= (int)radix) != 0) + ++digits; + + s = string; + + string += digits; + *string = '\0'; + + do { + + *--string = base36[value % radix]; + + } while ((value /= radix) != 0); + + + return string; +} + + + +/* + * ntohl() + * + * Description: + * Convert a ULONG from TCP/IP network order to host byte order (which is little-endian on Intel processors). + * + * Parameters: + * netlong - 32-bit number in TCP/IP network byte order. + * + * Returns: + * The value in host byte order. + * If the netlong parameter was already in host byte order, then no operation is performed. + */ + +ULONG +ntohl(IN ULONG netlong) +{ + ULONG result = 0; + + ((char *)&result)[0] = ((char *)&netlong)[3]; + ((char *)&result)[1] = ((char *)&netlong)[2]; + ((char *)&result)[2] = ((char *)&netlong)[1]; + ((char *)&result)[3] = ((char *)&netlong)[0]; + + return result; +} + + + +/* + * ntohs() + * + * Description: + * Convert a USHORT from TCP/IP network byte order to host byte order (which is little-endian on Intel processors). + * + * Parameters: + * netshort - 16-bit number in TCP/IP network byte order. + * + * Returns: + * The value in host byte order. + * If the netshort parameter was already in host byte order, then no operation is performed. + */ + +USHORT +ntohs(IN USHORT netshort) +{ + USHORT result = 0; + + ((char *)&result)[0] = ((char *)&netshort)[1]; + ((char *)&result)[1] = ((char *)&netshort)[0]; + + return result; +} + + +/* + * htonl() + * + * Description: + * Converts a ULONG from host to TCP/IP network byte order (which is big-endian). + * + * Parameters: + * hostlong - 32-bit number in host byte order. + * + * Returns: + * The value in TCP/IP's network byte order. + */ + +ULONG +htonl(ULONG hostlong) +{ + ULONG r1, r2, r3, r4; + + r1 = (hostlong >> 24); + r2 = (hostlong << 24); + r3 = (hostlong & 0xff00) << 8; + r4 = (hostlong >> 8) & 0xff00; + + return (r1 | r2 | r3 | r4); +} + + + +/* + * inet_aton() + * + * Description: + * Convert a ULONG from host to TCP/IP network byte order (which is big-endian). + * + * Parameters: + * hostlong - 32-bit number in host byte order. + * + * Returns: + * returns the value in TCP/IP's network byte order. + */ + +#define _islower(c) ( ((c) >= 'a' && (c) <= 'f') ) +#define _isxdigit(c) ( ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F') ) +#define _isascii(c) ( (c) <= 0177 ) +#define _isdigit(c) ( ((c) >= '0' && (c) <= '9') ) +#define _isspace(c) ( ((c) == ' ' ) ) + +//XXX verify... can invalid IPs be accepted as valid ones? + + +/* + * inet_addr() XXX rewrite + * + * Description: + * Convert a string containing an (IPv4) Internet Protocol dotted address into a ULONG (network order). + * + * Parameters: + * cp - Null-terminated character string representing a number expressed in the Internet + * standard ".'' (dotted) notation. + * + * Returns: + * A ULONG value containing a suitable binary representation of the Internet address given. + */ + +ULONG +inet_addr(PCCHAR cp) +{ + ULONG val; + int base, n; + char c; + unsigned int parts[4]; + unsigned int *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!_isdigit(c)) + return (0); + + val = 0; base = 10; +//XXX get rid of multiple base support? + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') + base = 16, c = *++cp; + else + base = 8; + } + + for (;;) { + if (_isascii(c) && _isdigit(c)) { + val = (val * base) + (c - '0'); + c = *++cp; + } else if (base == 16 && _isascii(c) && _isxdigit(c)) { + val = (val << 4) | + (c + 10 - (_islower(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && (!_isascii(c) || !_isspace(c))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + + return htonl(val); +} + + + +/* + * inet_ntoa() + * + * Description: + * Convert an (IPv4) Internet network address into a string in Internet standard dotted format. + * + * Parameters: + * ina - ULONG that represents an Internet host address. + * + * Returns: + * A character pointer to a static buffer containing the text address in standard ".'' notation. + */ + +//XXX not SMP safe +PCHAR +inet_ntoa2(ULONG ina) +{ + static char buf[4*sizeof "123"]; + unsigned char *ucp = (unsigned char *)&ina; + + sprintf(buf, "%d.%d.%d.%d", + ucp[0] & 0xff, + ucp[1] & 0xff, + ucp[2] & 0xff, + ucp[3] & 0xff); + + return buf; +} + +/* XXX move to netmisc.c or network.c */ +VOID +inet_ntoa(ULONG ina, PCHAR buf) +{ + unsigned char *ucp = (unsigned char *)&ina; + + sprintf(buf, "%d.%d.%d.%d", + ucp[0] & 0xff, + ucp[1] & 0xff, + ucp[2] & 0xff, + ucp[3] & 0xff); +} + + + +/* + * VerifyUnicodeString() + * + * Description: + * Make sure Unicode String buffer is readable. + * + * Parameters: + * InputUnicodeString - input unicode buffer. + * OutputUnicodeString - output buffer. + * + * Returns: + * TRUE to indicate success, FALSE is failed. + */ + +#define FINISH_VerifyUnicodeString(msg) \ + do { \ + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, msg); \ + return(FALSE); \ + } while(0) + +BOOLEAN +VerifyUnicodeString(IN PUNICODE_STRING InputUnicodeString, OUT PUNICODE_STRING OutputUnicodeString) +{ + if (! ARGUMENT_PRESENT(InputUnicodeString) ) + + FINISH_VerifyUnicodeString(("VerifyUnicodeString: Unicode string is NULL\n")); + + + /* this code is duplicated in pathproc.c!GetPathFromHandle for efficiency */ + __try + { + if (KeGetPreviousMode() != KernelMode) + { + ProbeForRead(InputUnicodeString, sizeof(UNICODE_STRING), sizeof(ULONG)); + + *OutputUnicodeString = ProbeAndReadUnicodeString(InputUnicodeString); + } + else + { + *OutputUnicodeString = *InputUnicodeString; + } + + + if (OutputUnicodeString->Length == 0) + + return FALSE; + + + if (((OutputUnicodeString->Length & (sizeof(WCHAR) - 1)) != 0) || + (OutputUnicodeString->Length > bMAX_PATH - sizeof(WCHAR)) ) + + FINISH_VerifyUnicodeString(("VerifyUnicodeString: invalid wchar string length = %d\n", OutputUnicodeString->Length)); + + + if (KeGetPreviousMode() != KernelMode) + + ProbeForRead(OutputUnicodeString->Buffer, OutputUnicodeString->Length, sizeof(WCHAR)); + } + + __except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("VerifyUnicodeString: caught an exception. status = 0x%x\n", status)); + + return(FALSE); + } + + return(TRUE); +} + + + +/* + * VerifyPwstr() + * + * Description: + * Make sure PWSTR buffer is readable. + * + * Parameters: + * InputString - input PWSTR buffer. + * InputStringLength - input string length. + * + * Returns: + * TRUE to indicate success, FALSE is failed. + */ + +#define FINISH_VerifyPwstr(msg) \ + do { \ + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, msg); \ + return(FALSE); \ + } while(0) + +BOOLEAN +VerifyPwstr(IN PWSTR InputString, IN ULONG InputStringLength) +{ + if (! ARGUMENT_PRESENT(InputString) ) + + FINISH_VerifyPwstr(("VerifyPwstr: Input string is NULL\n")); + + + if ((InputStringLength == 0) || + ((InputStringLength & (sizeof(WCHAR) - 1)) != 0) || + (InputStringLength > bMAX_PATH - sizeof(WCHAR)) ) + + FINISH_VerifyPwstr(("VerifyPwstr: invalid wchar string length = %d\n", InputStringLength)); + + + if (KeGetPreviousMode() != KernelMode) + { + __try + { + ProbeForRead(InputString, InputStringLength, sizeof(WCHAR)); + } + + __except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("VerifyPwstr: caught an exception. address=%x, status = 0x%x\n", InputString, status)); + + return(FALSE); + } + } + + + return(TRUE); +} + + + +/* + * ReadStringRegistryValue() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * TRUE to indicate success, FALSE is failed. + */ + +#define FINISH_ReadStringRegistryValue(msg) \ + do { \ + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, msg); \ + if (vpip) ExFreePoolWithTag(vpip, _POOL_TAG); \ + if (hKey) ZwClose(hKey); \ + return(NULL); \ + } while(0) + +PKEY_VALUE_PARTIAL_INFORMATION +ReadStringRegistryValue(IN PWSTR RegistryPath, IN PWSTR KeyName, OUT ULONG *VPIPSize) +{ + OBJECT_ATTRIBUTES oa; + HANDLE hKey = 0; + UNICODE_STRING usRegistryPath, usKeyName; + ULONG size; + NTSTATUS status; + PKEY_VALUE_PARTIAL_INFORMATION vpip = NULL; + PCHAR FunctionName = "ReadStringRegistryValue"; + + + if (RegistryPath == NULL || KeyName == NULL) + + FINISH_ReadStringRegistryValue(("%s: Received NULL parameter. %x %x\n", RegistryPath, KeyName)); + + + RtlInitUnicodeString(&usRegistryPath, RegistryPath); + + InitializeObjectAttributes(&oa, &usRegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + status = ZwOpenKey(&hKey, KEY_QUERY_VALUE , &oa); + if (! NT_SUCCESS(status)) + + FINISH_ReadStringRegistryValue(("%s: ZwOpenKey('%S') failed with status=%x\n", FunctionName, RegistryPath, status)); + + + RtlInitUnicodeString(&usKeyName, KeyName); + + status = ZwQueryValueKey(hKey, &usKeyName, KeyValuePartialInformation, NULL, 0, &size); + if (status == STATUS_OBJECT_NAME_NOT_FOUND || size == 0) + + FINISH_ReadStringRegistryValue(("%s: ZwQueryValueKey(%S) failed with status=%x\n", FunctionName, KeyName, status)); + + + vpip = (PKEY_VALUE_PARTIAL_INFORMATION) ExAllocatePoolWithTag(PagedPool, size, _POOL_TAG); + + if (!vpip) + + FINISH_ReadStringRegistryValue(("%s: out of memory\n", FunctionName)); + + + status = ZwQueryValueKey(hKey, &usKeyName, KeyValuePartialInformation, vpip, size, &size); + if (! NT_SUCCESS(status)) + + FINISH_ReadStringRegistryValue(("%s: ZwQueryValueKey2(%S) failed with status=%x\n", FunctionName, KeyName, status)); + + + if (vpip->Type != REG_SZ) + + FINISH_ReadStringRegistryValue(("%s: Wrong Type: %x\n", FunctionName, vpip->Type)); + + + ZwClose(hKey); + + + *VPIPSize = size; + + + return vpip; +} + + + +/* + * ReadStringRegistryValueA() + * + * Description: + * Read an ASCII value from the registry. + * + * Parameters: + * . + * + * Returns: + * TRUE to indicate success, FALSE is failed. + */ + +BOOLEAN +ReadStringRegistryValueA(IN PWSTR RegistryPath, IN PWSTR KeyName, OUT PCHAR Buffer, IN USHORT BufferSize) +{ + PKEY_VALUE_PARTIAL_INFORMATION vpip; + ULONG size; + + + ASSERT(Buffer); + + + if ((vpip = ReadStringRegistryValue(RegistryPath, KeyName, &size)) == NULL) + return FALSE; + + + if (_snprintf(Buffer, BufferSize, "%S", (PWSTR) &vpip->Data) < 0) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%s: Buffer is too small %d vs %d\n", BufferSize, size)); + ExFreePoolWithTag(vpip, _POOL_TAG); + return FALSE; + } + Buffer[BufferSize - 1] = 0; + + + ExFreePoolWithTag(vpip, _POOL_TAG); + + + return TRUE; +} + + + +/* + * ReadStringRegistryValueW() + * + * Description: + * Read a UNICODE value from the registry. + * + * Parameters: + * . + * + * Returns: + * TRUE to indicate success, FALSE is failed. + */ + +BOOLEAN +ReadStringRegistryValueW(IN PWSTR RegistryPath, IN PWSTR KeyName, OUT PWSTR Buffer, IN USHORT BufferSize) +{ + PKEY_VALUE_PARTIAL_INFORMATION vpip; + ULONG size; + + + ASSERT(Buffer); + + + if ((vpip = ReadStringRegistryValue(RegistryPath, KeyName, &size)) == NULL) + return FALSE; + + + if ((size - sizeof(KEY_VALUE_PARTIAL_INFORMATION)) + 1 > BufferSize) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%s: Buffer is too small %d vs %d\n", BufferSize, size)); + ExFreePoolWithTag(vpip, _POOL_TAG); + return FALSE; + } + + wcsncpy(Buffer, (PWSTR) vpip->Data, BufferSize); + Buffer[BufferSize - 1] = 0; + + + ExFreePoolWithTag(vpip, _POOL_TAG); + + + return TRUE; +} + + + +/* + * ReadSymlinkValue() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * TRUE to indicate success, FALSE is failed. + */ + +#define FINISH_ReadSymlinkValue(msg) \ + do { \ + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, msg); \ + if (hLink) ZwClose(hLink); \ + return(FALSE); \ + } while(0) + +BOOLEAN +ReadSymlinkValue(IN PWSTR SymlinkPath, OUT PCHAR Buffer, IN USHORT BufferSize) +{ + OBJECT_ATTRIBUTES oa; + HANDLE hLink = 0; + UNICODE_STRING usLink; + ANSI_STRING asLink; + WCHAR Link[MAX_PATH]; + ULONG size; + NTSTATUS status; + + + RtlInitUnicodeString(&usLink, SymlinkPath); + InitializeObjectAttributes(&oa, &usLink, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL); + + status = ZwOpenSymbolicLinkObject(&hLink, GENERIC_READ, &oa); + if (! NT_SUCCESS(status)) + + FINISH_ReadSymlinkValue(("ReadSymlinkValue: Unable to open '%S' link. status=%x\n", SymlinkPath, status)); + + + usLink.Buffer = Link; + usLink.MaximumLength = bMAX_PATH; + usLink.Length = 0; + + status = ZwQuerySymbolicLinkObject(hLink, &usLink, NULL); + if (! NT_SUCCESS(status)) + + FINISH_ReadSymlinkValue(("ReadSymlinkValue: Unable to query SystemRoot link. status=%x\n", status)); + + + asLink.Length = 0; + asLink.MaximumLength = BufferSize; + asLink.Buffer = Buffer; + + RtlUnicodeStringToAnsiString(&asLink, &usLink, FALSE); + asLink.Buffer[asLink.Length] = '\0'; + + + ZwClose(hLink); + + return TRUE; +} + + + +/* + * RunPostBootup() + * + * Description: + * Initializes any subsystems that require to be initialized after the system finished booting up. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +VOID +InitPostBootup() +{ + ASSERT(BootingUp == FALSE); + + ProcessPostBootup(); + LogPostBootup(); + PolicyPostBootup(); + UserlandPostBootup(); +} + + + +/* + * GetCurrentUserSid() + * + * Description: + * Retrieves current user's SID. + * + * Parameters: + * Size - Extra number of bytes to allocate [IN]. Total number of bytes allocated [OUT]. + * + * Returns: + * Pointer to an allocated memory block, the actual SID is at offset +Size. + * NULL is failed. + */ + +typedef struct _TOKEN_GROUPS { + DWORD GroupCount; + SID_AND_ATTRIBUTES Groups[ANYSIZE_ARRAY]; +} TOKEN_GROUPS, *PTOKEN_GROUPS; + +PCHAR +GetCurrentUserSid(PUSHORT Size) // IN OUT +{ + HANDLE TokenHandle; + NTSTATUS status; + ULONG TokenSize, tmp; + PCHAR UserInfo; + PSID_AND_ATTRIBUTES psa; +// PTOKEN_GROUPS ptg; +// PSID ps; + + + status = ZwOpenThreadToken(CURRENT_THREAD, TOKEN_QUERY, FALSE, &TokenHandle); + + if (! NT_SUCCESS(status)) + { + if (status != STATUS_NO_TOKEN) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%d GetCurrentUserSid: ZwOpenThreadToken failed with status %x\n", status)); + return NULL; + } + + status = ZwOpenProcessToken(CURRENT_PROCESS, TOKEN_QUERY, &TokenHandle); + + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%d GetCurrentUserSid: ZwOpenProcessToken failed with status %x\n", status)); + return NULL; + } + } + + + /* first, find out the total amount of token information to be returned */ + + status = ZwQueryInformationToken(TokenHandle, TokenUser, &TokenSize, 0, &TokenSize); +// status = ZwQueryInformationToken(TokenHandle, TokenGroups, &TokenSize, 0, &TokenSize); +// status = ZwQueryInformationToken(TokenHandle, TokenPrimaryGroup, &TokenSize, 0, &TokenSize); + if (status != STATUS_BUFFER_TOO_SMALL || TokenSize == 0) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("GetCurrentUserSid: ZwQueryInformationToken failed. status=%x size=%d\n", status, TokenSize)); + return NULL; + } + + + /* second, allocate the required amount of memory */ + + UserInfo = ExAllocatePoolWithTag(NonPagedPool, TokenSize + *Size, _POOL_TAG); + if (UserInfo == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("GetCurrentUserSid: out of memory (requested %d bytes)\n", TokenSize + *Size)); + return NULL; + } + + + psa = (PSID_AND_ATTRIBUTES) (UserInfo + *Size); +// ptg = (PTOKEN_GROUPS) (UserInfo + *Size); +// ps = (PSID) (UserInfo + *Size); + + + /* third, request the token information */ + + status = ZwQueryInformationToken(TokenHandle, TokenUser, psa, TokenSize, &tmp); +// status = ZwQueryInformationToken(TokenHandle, TokenGroups, psa, TokenSize, &tmp); +// status = ZwQueryInformationToken(TokenHandle, TokenPrimaryGroup, ps, TokenSize, &tmp); + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("GetCurrentUserSid: ZwQueryInformationToken failed with status %x\n", status)); + ExFreePoolWithTag(UserInfo, _POOL_TAG); + return NULL; + } + + ZwClose(TokenHandle); + + + /* convert absolute SID into a relative one */ + psa->Sid = (PSID) ( (PCHAR) psa->Sid - (PCHAR) psa ); +/* + for (tmp = 0; tmp < ptg->GroupCount; tmp++) + { + ptg->Groups[tmp].Sid = (PSID) ( (PCHAR) ptg->Groups[tmp].Sid - (PCHAR) ptg->Groups ); + } +*/ +// * (PULONG) ps = 4; + + /* return size value */ + *Size += (USHORT) TokenSize; + + return UserInfo; +} + + + +/* + * NOTE: Adapted from "The VTrace Tool: Building a System Tracer for Windows NT and Windows 2000" -- MSDN Magazine, October 2000 + * NOTE2: This function should only be called on Windows 2000+. + * It's not necessary on Windows NT, and, besides, on Windows NT 4.0 SP3 and earlier, + * MmMapLockedPages() does not give the result we need. + */ + +PVOID +ExchangeReadOnlyMemoryPointer(IN OUT PVOID *Target, IN PVOID Value) +{ + PMDL MDL; + PVOID MappedAddress, ReturnValue; + + + MDL = IoAllocateMdl(Target, sizeof(Value), FALSE, FALSE, NULL); + + if (MDL == NULL) + return NULL; + + +// MmBuildMdlForNonPagedPool(MDL); + + + /* + * Calls to MmProbeAndLockPages must be enclosed in a try/except block. + * If the pages do not support the specified operation, the routine raises the + * STATUS_ACCESS_VIOLATION exception. + */ + + __try + { + MmProbeAndLockPages(MDL, KernelMode, IoModifyAccess); + } + + __except(EXCEPTION_EXECUTE_HANDLER) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("ExchangeReadOnlyMemoryPointer(%x, %x): Caught an exception\n", Target, Value)); + IoFreeMdl(MDL); + return NULL; + } + + +// MappedAddress = MmMapLockedPages(MDL, KernelMode); + MappedAddress = MmMapLockedPagesSpecifyCache(MDL, KernelMode, MmNonCached, NULL, FALSE, NormalPagePriority); + + if (MappedAddress == NULL) + { + MmUnlockPages(MDL); + IoFreeMdl(MDL); + return NULL; + } + + + ReturnValue = InterlockedExchangePointer(MappedAddress, Value); + + + MmUnmapLockedPages(MappedAddress, MDL); + MmUnlockPages(MDL); + IoFreeMdl(MDL); + + + return ReturnValue; +} diff --git a/misc.h b/misc.h new file mode 100644 index 0000000..e49e28d --- /dev/null +++ b/misc.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * misc.h + * + * Abstract: + * + * This module definies various types used by miscellaneous routines. + * + * Author: + * + * Eugene Tsyrklevich 23-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __MISC_H__ +#define __MISC_H__ + + +#include +#include "pathproc.h" +#include "log.h" + + +// win2k ddk does not know about ExFreePoolWithTag? +/* +#ifndef ExFreePoolWithTag +#define ExFreePoolWithTag(p, tag) ExFreePool(p) +#endif +*/ + +#define _POOL_TAG 'nozO' + + +/* convert seconds into units of 100 nanoseconds for KeWaitFor* & KeDelayExecutionThread functions */ +#define SECONDS(s) ((s) * -10000000) + + +#define ProbeAndReadUnicodeString(Source) \ + (((Source) >= (UNICODE_STRING * const)MM_USER_PROBE_ADDRESS) ? \ + (*(volatile UNICODE_STRING * const)MM_USER_PROBE_ADDRESS) : (*(volatile UNICODE_STRING *)(Source))) + + +/* extern defines */ + +KPROCESSOR_MODE +KeGetPreviousMode( + VOID + ); + + +int sprintf(char *buffer, const char *format, ...); +int _snprintf(char *buffer, size_t count, const char *format, ...); +int _snwprintf(wchar_t *buffer, size_t count, const wchar_t *format, ...); + +BOOLEAN LearningModePostBootup(); + + +#define CURRENT_PROCESS_PID ((ULONG) PsGetCurrentProcessId()) + + +#define ON 1 +#define OFF 0 + + +/* internal defines */ + +extern BOOLEAN BootingUp; + + +INT32 atoi(IN PCHAR buf); +PCHAR itoa(int value, char *string, unsigned int radix); + +/* XXX move to netmisc.c */ +ULONG ntohl(IN ULONG netlong); +USHORT ntohs(IN USHORT netshort); + +ULONG inet_addr(IN PCCHAR cp); +VOID inet_ntoa(ULONG ina, PCHAR buf); +PCHAR inet_ntoa2(IN ULONG ina); + +BOOLEAN VerifyUnicodeString(IN PUNICODE_STRING InputUnicodeString, OUT PUNICODE_STRING OutputUnicodeString); +BOOLEAN VerifyPwstr(IN PWSTR InputString, IN ULONG InputStringLength); + +BOOLEAN ReadStringRegistryValueA(IN PWSTR RegistryPath, IN PWSTR KeyName, OUT PCHAR Buffer, IN USHORT BufferSize); +BOOLEAN ReadStringRegistryValueW(IN PWSTR RegistryPath, IN PWSTR KeyName, OUT PWSTR Buffer, IN USHORT BufferSize); +BOOLEAN ReadSymlinkValue(IN PWSTR SymlinkPath, OUT PCHAR Buffer, IN USHORT BufferSize); + +VOID InitPostBootup(); +PCHAR GetCurrentUserSid(PUSHORT Size); + +PVOID ExchangeReadOnlyMemoryPointer(IN OUT PVOID *Target, IN PVOID Value); + + +#endif /* __MISC_H__ */ \ No newline at end of file diff --git a/mutant.c b/mutant.c new file mode 100644 index 0000000..ec324ca --- /dev/null +++ b/mutant.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * mutant.c + * + * Abstract: + * + * This module implements various mutant (mutex) hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include "mutant.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitMutantHooks) +#endif + + +fpZwCreateMutant OriginalNtCreateMutant = NULL; +fpZwOpenMutant OriginalNtOpenMutant = NULL; + + +/* + * HookedNtCreateMutant() + * + * Description: + * This function mediates the NtCreateMutant() system service and checks the + * provided mutant name against the global and current process security policies. + * + * NOTE: ZwCreateMutant creates or opens a mutant object. [NAR] + * + * Parameters: + * Those of NtCreateMutant(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateMutant(). + */ + +NTSTATUS +NTAPI +HookedNtCreateMutant +( + OUT PHANDLE MutantHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN BOOLEAN InitialOwner +) +{ + PCHAR FunctionName = "HookedNtCreateMutant"; + + + HOOK_ROUTINE_START(MUTANT); + + + ASSERT(OriginalNtCreateMutant); + + rc = OriginalNtCreateMutant(MutantHandle, DesiredAccess, ObjectAttributes, InitialOwner); + + + HOOK_ROUTINE_FINISH(MUTANT); +} + + + +/* + * HookedNtOpenMutant() + * + * Description: + * This function mediates the NtOpenMutant() system service and checks the + * provided mutant name against the global and current process security policies. + * + * NOTE: ZwOpenMutant opens a mutant object. [NAR] + * + * Parameters: + * Those of NtOpenMutant(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtOpenMutant(). + */ + +NTSTATUS +NTAPI +HookedNtOpenMutant +( + OUT PHANDLE MutantHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtOpenMutant"; + + + HOOK_ROUTINE_START(MUTANT); + + + ASSERT(OriginalNtOpenMutant); + + rc = OriginalNtOpenMutant(MutantHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(MUTANT); +} + + + +/* + * InitMutantHooks() + * + * Description: + * Initializes all the mediated mutant operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitMutantHooks() +{ + if ( (OriginalNtCreateMutant = (fpZwCreateMutant) ZwCalls[ZW_CREATE_MUTANT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_MUTANT, LOG_PRIORITY_DEBUG, ("InitMutantHooks: OriginalNtCreateMutant is NULL\n")); + return FALSE; + } + + if ( (OriginalNtOpenMutant = (fpZwOpenMutant) ZwCalls[ZW_OPEN_MUTANT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_MUTANT, LOG_PRIORITY_DEBUG, ("InitMutantHooks: OriginalNtOpenMutant is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/mutant.h b/mutant.h new file mode 100644 index 0000000..ab801fa --- /dev/null +++ b/mutant.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * mutant.h + * + * Abstract: + * + * This module defines various types used by mutant (mutex) hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __MUTANT_H__ +#define __MUTANT_H__ + + +#include +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" +#include "log.h" + + +/* + * ZwCreateMutant creates or opens a mutant object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateMutant) ( + OUT PHANDLE MutantHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN BOOLEAN InitialOwner + ); + +NTSTATUS +NTAPI +HookedNtCreateMutant( + OUT PHANDLE MutantHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN BOOLEAN InitialOwner + ); + + +/* + * ZwOpenMutant opens a mutant object. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenMutant) ( + OUT PHANDLE MutantHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS +NTAPI +HookedNtOpenMutant( + OUT PHANDLE MutantHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +BOOLEAN InitMutantHooks(); + + +#endif /* __MUTANT_H__ */ diff --git a/network.c b/network.c new file mode 100644 index 0000000..3564db2 --- /dev/null +++ b/network.c @@ -0,0 +1,715 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * network.c + * + * Abstract: + * + * This module defines various routines used for hooking the Transport Driver Interface (TDI) network routines. + * + * Author: + * + * Eugene Tsyrklevich 12-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include +#include +#include "network.h" +#include "hookproc.h" +#include "userland.h" +#include "learn.h" +#include "policy.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InstallNetworkHooks) +#pragma alloc_text (PAGE, RemoveNetworkHooks) +#endif + + +//XXX fast io is not handled. TdiDispatchFastDeviceControl + + +PDEVICE_OBJECT pTcpDevice = NULL, pTcpDeviceOriginal = NULL; +PDEVICE_OBJECT pUdpDevice = NULL, pUdpDeviceOriginal = NULL; +PDEVICE_OBJECT pIpDevice = NULL, pIpDeviceOriginal = NULL; + +#if DBG +int HookedTDIRunning = 0; +#endif + + +/* + * TdiStub() XXX remove + * + * Description: + * . + * + * Parameters: + * pIrp - IRP (I/O Request Packet) request. + * pIrpStack - . + * pCompletion - . + * + * Returns: + * STATUS_SUCCESS. + */ + +NTSTATUS +TdiStub(IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpStack, OUT PTDI_CALLBACK pCompletion, IN ULONG DeviceType) +{ +// LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("TdiStub(%x %x %x)\n", pIrp, pIrpStack, pCompletion)); + return STATUS_SUCCESS; +} + + + +NTSTATUS +TdiSetEventHandler(IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpStack, OUT PTDI_CALLBACK pCompletion, IN ULONG DeviceType) +{ + PTDI_REQUEST_KERNEL_SET_EVENT r = (PTDI_REQUEST_KERNEL_SET_EVENT) &pIrpStack->Parameters; + + + if (r->EventType != TDI_EVENT_CONNECT) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("%d TdiSetEventHandler: %x %x %x\n", CURRENT_PROCESS_PID, r->EventType, r->EventHandler, r->EventContext)); + return STATUS_SUCCESS; + } + + + if (r->EventHandler == NULL) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("%d TdiSetEventHandler: TDI_EVENT_CONNECT deregistration %x %x %x\n", CURRENT_PROCESS_PID, r->EventHandler, r->EventContext, pIrpStack->FileObject)); + return STATUS_SUCCESS; + } + + + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("%d TdiSetEventHandler: TDI_EVENT_CONNECT %x %x %x\n", CURRENT_PROCESS_PID, r->EventHandler, r->EventContext, pIrpStack->FileObject)); + + + return STATUS_SUCCESS; +} + + + +NTSTATUS +TdiConnect(IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpStack, OUT PTDI_CALLBACK pCompletion, IN ULONG DeviceType) +{ + /* + * IrpSp->Parameters + * + * Pointer to a TDI_REQUEST_KERNEL_CONNECT structure, equivalent to the TDI_REQUEST_KERNEL structure. + */ + + PTDI_REQUEST_KERNEL_CONNECT ConnectInfo = (PTDI_REQUEST_KERNEL_CONNECT) &pIrpStack->Parameters; + PTRANSPORT_ADDRESS pTransportAddress; + PTA_ADDRESS pAddress; + PTDI_ADDRESS_IP ip; + CHAR NETWORKNAME[MAX_PATH]; + PCHAR FunctionName = "TdiConnect"; + + + HOOK_ROUTINE_ENTER(); + + + if (! MmIsAddressValid(ConnectInfo) || + ! MmIsAddressValid(ConnectInfo->RequestConnectionInformation) || + ! MmIsAddressValid(ConnectInfo->RequestConnectionInformation->RemoteAddress)) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("TdiConnect: MmIsAddressValid failed\n")); + HOOK_ROUTINE_EXIT(STATUS_SUCCESS); + } + + + pTransportAddress = (PTRANSPORT_ADDRESS) ConnectInfo->RequestConnectionInformation->RemoteAddress; + + pAddress = (PTA_ADDRESS) pTransportAddress->Address; + + /* verify that the specified address is a single IP address */ + if (pTransportAddress->TAAddressCount != 1 || + pAddress->AddressType != TDI_ADDRESS_TYPE_IP || + pAddress->AddressLength != TDI_ADDRESS_LENGTH_IP) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("%d TdiConnect: Invalid address detected\n", CURRENT_PROCESS_PID)); + HOOK_ROUTINE_EXIT(STATUS_SUCCESS); + } + + ip = (PTDI_ADDRESS_IP) &pAddress->Address; + + + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("%d TdiConnect(%x %x %x). %d %x:%u (%s)\n", (ULONG) PsGetCurrentProcessId(), pIrp, pIrpStack, pCompletion, pTransportAddress->TAAddressCount, ntohl(ip->in_addr), ntohs(ip->sin_port), inet_ntoa2(ip->in_addr))); + + + inet_ntoa(ip->in_addr, NETWORKNAME); + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(NETWORK, DeviceType == NET_DEVICE_TYPE_TCP ? OP_TCPCONNECT : OP_UDPCONNECT); + } + else + { + // learning mode + AddRule(RULE_NETWORK, NETWORKNAME, DeviceType == NET_DEVICE_TYPE_TCP ? OP_TCPCONNECT : OP_UDPCONNECT); + } + + + HOOK_ROUTINE_EXIT(STATUS_SUCCESS); +} + + + +NTSTATUS +TdiListen(IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpStack, OUT PTDI_CALLBACK pCompletion, IN ULONG DeviceType) +{ + /* + * IrpSp->Parameters + * + * Pointer to a TDI_REQUEST_KERNEL_LISTEN structure, equivalent to the TDI_REQUEST_KERNEL structure. + */ + + PTDI_REQUEST_KERNEL_LISTEN ListenInfo = (PTDI_REQUEST_KERNEL_LISTEN) &pIrpStack->Parameters; + PTRANSPORT_ADDRESS pTransportAddress; + PTA_ADDRESS pAddress; + PTDI_ADDRESS_IP ip; + + + if (! MmIsAddressValid(ListenInfo) || + ! MmIsAddressValid(ListenInfo->RequestConnectionInformation) || + ! MmIsAddressValid(ListenInfo->RequestConnectionInformation->RemoteAddress)) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("TdiListen: MmIsAddressValid failed\n")); + HOOK_ROUTINE_EXIT(STATUS_SUCCESS); + } + + pTransportAddress = (PTRANSPORT_ADDRESS) ListenInfo->RequestConnectionInformation->RemoteAddress; + + pAddress = (PTA_ADDRESS) pTransportAddress->Address; + + /* verify that the specified address is a single IP address */ + if (pTransportAddress->TAAddressCount != 1 || + pAddress->AddressType != TDI_ADDRESS_TYPE_IP || + pAddress->AddressLength != TDI_ADDRESS_LENGTH_IP) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("%d TdiListen: Invalid address detected\n", CURRENT_PROCESS_PID)); + HOOK_ROUTINE_EXIT(STATUS_SUCCESS); + } + + ip = (PTDI_ADDRESS_IP) &pAddress->Address; + + + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("TdiListen(%x %x %x). %d %x:%u (%s)\n", pIrp, pIrpStack, pCompletion, pTransportAddress->TAAddressCount, ntohl(ip->in_addr), ntohs(ip->sin_port), inet_ntoa2(ip->in_addr))); + + + return STATUS_SUCCESS; +} + + + +NTSTATUS +TdiAccept(IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpStack, OUT PTDI_CALLBACK pCompletion, IN ULONG DeviceType) +{ + /* + * IrpSp->Parameters + * + * Specifies a TDI_REQUEST_KERNEL_ACCEPT structure. + */ + + PTDI_REQUEST_KERNEL_ACCEPT AcceptInfo = (PTDI_REQUEST_KERNEL_ACCEPT) &pIrpStack->Parameters; + PTRANSPORT_ADDRESS pTransportAddress = (PTRANSPORT_ADDRESS) AcceptInfo->RequestConnectionInformation->RemoteAddress; + PTA_ADDRESS pAddress = (PTA_ADDRESS) pTransportAddress->Address; + PTDI_ADDRESS_IP ip = (PTDI_ADDRESS_IP) &pAddress->Address; + + + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("TdiAccept(%x %x %x). %d %x:%u (%s)\n", pIrp, pIrpStack, pCompletion, pTransportAddress->TAAddressCount, ntohl(ip->in_addr), ntohs(ip->sin_port), inet_ntoa2(ip->in_addr))); + + + return STATUS_SUCCESS; +} + + +/* +NTSTATUS +GenericCompletion(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pContext) +{ + if (pIrp->PendingReturned ) + IoMarkIrpPending(pIrp); + + return STATUS_SUCCESS; +} +*/ + + +TDI_IOCTL TdiIoctl[] = +{ + { TDI_ASSOCIATE_ADDRESS, "TDI_ASSOCIATE_ADDRESS", TdiStub }, + { TDI_DISASSOCIATE_ADDRESS, "TDI_DISASSOCIATE_ADDRESS", TdiStub }, + { TDI_CONNECT, "TDI_CONNECT", TdiConnect }, + { TDI_LISTEN, "TDI_LISTEN", TdiListen }, + { TDI_ACCEPT, "TDI_ACCEPT", TdiAccept }, + { TDI_DISCONNECT, "TDI_DISCONNECT", TdiStub }, + { TDI_SEND, "TDI_SEND", TdiStub }, + { TDI_RECEIVE, "TDI_RECEIVE", TdiStub }, + { TDI_SEND_DATAGRAM, "TDI_SEND_DATAGRAM", TdiStub }, + { TDI_RECEIVE_DATAGRAM, "TDI_RECEIVE_DATAGRAM", TdiStub }, + { TDI_SET_EVENT_HANDLER, "TDI_SET_EVENT_HANDLER", TdiSetEventHandler }, + { TDI_QUERY_INFORMATION, "TDI_QUERY_INFORMATION", TdiStub }, + { TDI_SET_INFORMATION, "TDI_SET_INFORMATION", TdiStub }, + { TDI_ACTION, "TDI_ACTION", TdiStub }, + { TDI_DIRECT_SEND, "TDI_DIRECT_SEND", TdiStub }, + { TDI_DIRECT_SEND_DATAGRAM, "TDI_DIRECT_SEND_DATAGRAM", TdiStub }, +}; + + + +//XXX this function can be called from HookedNtCreateFile (-> NtCreateFile -> IoCreateFile -> ObOpenObjectbyName -> ... -> TDI) +BOOLEAN +TDIDispatch(PDEVICE_OBJECT pDeviceObject, PIRP pIrp, NTSTATUS *status) +{ + PIO_STACK_LOCATION pIrpStack; + TDI_CALLBACK Callback; + ULONG DeviceType = 0; + + + if (pDeviceObject == pTcpDevice) + { + DeviceType = NET_DEVICE_TYPE_TCP; + } + else if (pDeviceObject == pUdpDevice) + { + DeviceType = NET_DEVICE_TYPE_UDP; + } + else if (pDeviceObject == pIpDevice) + { + DeviceType = NET_DEVICE_TYPE_IP; + } + else + { + return FALSE; + } + + + HOOK_TDI_ENTER_NORC(); + + + pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + + + memset(&Callback, 0, sizeof(Callback)); + +// if (pIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER) +// { +// LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER\n")); +// } + + switch (pIrpStack->MajorFunction) + { + case IRP_MJ_CREATE: + + *status = TDICreate(pDeviceObject, pIrp, pIrpStack, &Callback); + + break; + + + case IRP_MJ_DEVICE_CONTROL: + +// if (DeviceType == NET_DEVICE_TYPE_IP && pIrpStack->Parameters.DeviceIoControl.IoControlCode == 0x120000) + if (DeviceType == NET_DEVICE_TYPE_IP) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("%d pIpDevice in use (%x %x %x)\n", (ULONG) PsGetCurrentProcessId(), pIrpStack->Parameters.DeviceIoControl.IoControlCode, pIrpStack->MajorFunction, pIrpStack->MinorFunction)); +// *status = STATUS_ACCESS_DENIED; + break; + } + + if (KeGetCurrentIrql() != PASSIVE_LEVEL || ! NT_SUCCESS(TdiMapUserRequest(pDeviceObject, pIrp, pIrpStack))) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("TdiMapUserRequest failed: %x (irql %d)\n", pIrpStack->Parameters.DeviceIoControl.IoControlCode, KeGetCurrentIrql())); + break; + } + + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("IRP_MJ_DEVICE_CONTROL2 %x\n", pIrpStack->Parameters.DeviceIoControl.IoControlCode)); + + /* FALLTHROUGH */ + + + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + { + int i; + + if (DeviceType == NET_DEVICE_TYPE_IP) + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("%d pIpDevice in use2\n", (ULONG) PsGetCurrentProcessId())); + + for (i = 0; i < sizeof(TdiIoctl) / sizeof(TdiIoctl[0]); i++) + { + if (TdiIoctl[i].MinorFunction == pIrpStack->MinorFunction) + { + if (TdiIoctl[i].pfRoutine == TdiStub) + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("%d IRP_MJ_INTERNAL_DEVICE_CONTROL %s\n", (ULONG) PsGetCurrentProcessId(), TdiIoctl[i].Description)); + + *status = TdiIoctl[i].pfRoutine(pIrp, pIrpStack, &Callback, DeviceType); + + break; + } + } + + break; + } + + case IRP_MJ_CLEANUP: + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("IRP_MJ_CLEANUP\n")); + break; + + case IRP_MJ_CLOSE: + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("IRP_MJ_CLOSE\n")); + break; + + default: + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("TDIDispatch: default switch case triggered\n")); + break; + } + + + if (*status == STATUS_ACCESS_DENIED) + { + pIrp->IoStatus.Status = STATUS_ACCESS_DENIED; + IoCompleteRequest (pIrp, IO_NO_INCREMENT); + + HOOK_TDI_EXIT(TRUE); + } + + + if (Callback.Routine) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("TDI Callback.Routine\n")); + + //XXX IoCopyCurrentIrpStackLocationToNext() + IoSetCompletionRoutine(pIrp, Callback.Routine, Callback.Context, TRUE, TRUE, TRUE); + } + else + { + // Set up a completion routine to handle the bubbling of the "pending" mark of an IRP +// IoSetCompletionRoutine(pIrp, GenericCompletion, NULL, TRUE, TRUE, TRUE); + + IoSkipCurrentIrpStackLocation(pIrp); + } + + + if (DeviceType == NET_DEVICE_TYPE_TCP) + { + *status = IoCallDriver(pTcpDeviceOriginal, pIrp); + } + else if (DeviceType == NET_DEVICE_TYPE_UDP) + { + *status = IoCallDriver(pUdpDeviceOriginal, pIrp); + } + else if (DeviceType == NET_DEVICE_TYPE_IP) + { + *status = IoCallDriver(pIpDeviceOriginal, pIrp); + } + else + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("TDIDispatch: Unknown device type\n")); + } + + + HOOK_TDI_EXIT(TRUE); +} + + + +NTSTATUS +TDICreateAddressCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp, IN PVOID Context) +{ + return STATUS_SUCCESS; +} + + + +NTSTATUS +TDICreate(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpStack, OUT PTDI_CALLBACK pCompletion) +{ + FILE_FULL_EA_INFORMATION *ea = (FILE_FULL_EA_INFORMATION *) pIrp->AssociatedIrp.SystemBuffer; + + + HOOK_ROUTINE_ENTER(); + + + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("TDICreate(%x %x %x)\n", pIrp, pIrpStack, ea)); + + + /* + * From DDK (TdiDispatchCreate): + * + * Irp->AssociatedIrp.SystemBuffer + * + * Pointer to a FILE_FULL_EA_INFORMATION-structured buffer if the file object represents an address or a + * connection endpoint to be opened. + * For an address, the EaName member is set to the system-defined constant TdiTransportAddress and the EA value + * following the EaName array is of type TRANSPORT_ADDRESS, set up by the client to specify the address to be + * opened. For some transports, this value can be a symbolic netBIOS or DNS name to be translated by the transport. + * + * For a connection endpoint, the EaName member is set to the system-defined constant TdiConnectionContext and + * the EA value following the EaName array is a client-supplied handle, opaque to the transport driver. The + * transport must save this handle and subsequently pass it back to the client's registered event handlers for + * this connection. + * + * If the given file object represents a control channel, this member is NULL. + */ + + if (ea == NULL) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("TDICreate: Control channel\n")); + HOOK_ROUTINE_EXIT(STATUS_SUCCESS); + } + + + if (! MmIsAddressValid(ea) || ea->EaName == NULL || ! MmIsAddressValid(ea->EaName)) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("TDICreate: MmIsAddressValid() failed\n")); + HOOK_ROUTINE_EXIT(STATUS_SUCCESS); + } + + + if (ea->EaNameLength == TDI_CONNECTION_CONTEXT_LENGTH && + memcmp(ea->EaName, TdiConnectionContext, TDI_CONNECTION_CONTEXT_LENGTH) == 0) + { + CONNECTION_CONTEXT conn_ctx = *(CONNECTION_CONTEXT *) (ea->EaName + ea->EaNameLength + 1); + + if (conn_ctx) + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("TDI Connection object 0x%x %x\n", conn_ctx, * (PULONG) conn_ctx)); + + HOOK_ROUTINE_EXIT(STATUS_SUCCESS); + } + + + // NOTE: for RawIp you can extract protocol number from irps->FileObject->FileName + + if (ea->EaNameLength == TDI_TRANSPORT_ADDRESS_LENGTH && + memcmp(ea->EaName, TdiTransportAddress, TDI_TRANSPORT_ADDRESS_LENGTH) == 0) + { + PTRANSPORT_ADDRESS pTransportAddress; + PTA_ADDRESS pAddress; + PIRP QueryIrp; + int i; + + + pTransportAddress = (PTRANSPORT_ADDRESS) (ea->EaName + ea->EaNameLength + 1); + pAddress = pTransportAddress->Address; + + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("TDICreate: TDI Address object. Num %d\n", pTransportAddress->TAAddressCount)); + + + for (i = 0; i < pTransportAddress->TAAddressCount; i++) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("TDICreate: TDI Address %d: %x %x\n", i, pAddress->AddressLength, pAddress->AddressType)); + + + if (pAddress->AddressType == TDI_ADDRESS_TYPE_IP) + { + PTDI_ADDRESS_IP ip = (PTDI_ADDRESS_IP) &pAddress->Address; + CHAR NETWORKNAME[MAX_PATH]; + PCHAR FunctionName = "TDICreate"; + + + if (ip->sin_port != 0) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("%d TDICreate: Bind IP %x:%u (%s)\n", (ULONG) PsGetCurrentProcessId(), ntohl(ip->in_addr), ntohs(ip->sin_port), inet_ntoa2(ip->in_addr))); + + itoa( ntohs(ip->sin_port), NETWORKNAME, 10 ); + //inet_ntoa(ip->in_addr, NETWORKNAME); + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(NETWORK, OP_BIND); + } + else + { + // learning mode + AddRule(RULE_NETWORK, NETWORKNAME, OP_BIND); + } + } + else + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_VERBOSE, ("%d TDICreate: IP & port are both zero\n", (ULONG) PsGetCurrentProcessId())); + } + } + else + { + //XXX fail if only IP network addresses are allowed. + } + + pAddress += 1; + } + + //XXX reread WDM ch 5.3 "COmpleting I/O requests" +/* + QueryIrp = TdiBuildInternalDeviceControlIrp(TDI_QUERY_INFORMATION, pDeviceObject, + pIrpStack->FileObject, NULL, NULL); + if (QueryIrp == NULL) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("TDICreate: QueryIrp is NULL\n")); + return FALSE; + } + + pCompletion->Routine = TDICreateAddressCompletion; + pCompletion->Context = QueryIrp; +*/ + } + + + HOOK_ROUTINE_EXIT(STATUS_SUCCESS); +} + + + +/* + * InstallNetworkHooks() + * + * Description: + * . + * + * NOTE: Called once during driver initialization (DriverEntry()). + * There is no need to cleanup in case a failure since RemoveNetworkHooks() will be called later. + * + * Parameters: + * pDriverObject - pointer to a driver object that represents this driver. + * + * Returns: + * STATUS_SUCCESS to indicate success or an NTSTATUS error code if failed. + */ + +NTSTATUS +InstallNetworkHooks(PDRIVER_OBJECT pDriverObject) +{ + UNICODE_STRING Name; + NTSTATUS status; + + + status = IoCreateDevice(pDriverObject, 0, NULL, FILE_DEVICE_UNKNOWN, 0, TRUE, &pTcpDevice); + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("InstallNetworkHooks: IoCreateDevice(tcp) failed\n")); + return status; + } + + + status = IoCreateDevice(pDriverObject, 0, NULL, FILE_DEVICE_UNKNOWN, 0, TRUE, &pUdpDevice); + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("InstallNetworkHooks: IoCreateDevice(udp) failed\n")); + return status; + } + + + status = IoCreateDevice(pDriverObject, 0, NULL, FILE_DEVICE_UNKNOWN, 0, TRUE, &pIpDevice); + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("InstallNetworkHooks: IoCreateDevice(udp) failed\n")); + return status; + } + + + RtlInitUnicodeString(&Name, L"\\Device\\Tcp"); + + status = IoAttachDevice(pTcpDevice, &Name, &pTcpDeviceOriginal); + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("InstallNetworkHooks: IoAttachDevice(\\Device\\Tcp) failed\n")); + return status; + } + + + RtlInitUnicodeString(&Name, L"\\Device\\Udp"); + + status = IoAttachDevice(pUdpDevice, &Name, &pUdpDeviceOriginal); + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("InstallNetworkHooks: IoAttachDevice(\\Device\\Udp) failed\n")); + return status; + } + + + RtlInitUnicodeString(&Name, L"\\Device\\Ip"); + + status = IoAttachDevice(pIpDevice, &Name, &pIpDeviceOriginal); + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_NETWORK, LOG_PRIORITY_DEBUG, ("InstallNetworkHooks: IoAttachDevice(\\Device\\Ip) failed\n")); + return status; + } + + + pTcpDevice->StackSize = pTcpDeviceOriginal->StackSize + 1; + // XXX Flags &= ~DO_DEVICE_INITIALIZING; + pTcpDevice->Flags |= pTcpDeviceOriginal->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE | DO_POWER_INRUSH) & ~DO_DEVICE_INITIALIZING; + pTcpDevice->DeviceType = pTcpDeviceOriginal->DeviceType; + pTcpDevice->Characteristics = pTcpDeviceOriginal->Characteristics; + + + pUdpDevice->StackSize = pUdpDeviceOriginal->StackSize + 1; + pUdpDevice->Flags |= pUdpDeviceOriginal->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE | DO_POWER_INRUSH) & ~DO_DEVICE_INITIALIZING; + pUdpDevice->DeviceType = pUdpDeviceOriginal->DeviceType; + pUdpDevice->Characteristics = pUdpDeviceOriginal->Characteristics; + + + pIpDevice->StackSize = pIpDeviceOriginal->StackSize + 1; + pIpDevice->Flags |= pIpDeviceOriginal->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE | DO_POWER_INRUSH) & ~DO_DEVICE_INITIALIZING; + pIpDevice->DeviceType = pIpDeviceOriginal->DeviceType; + pIpDevice->Characteristics = pIpDeviceOriginal->Characteristics; + + + return STATUS_SUCCESS; +} + + + +/* + * RemoveNetworkHooks() + * + * Description: + * Detach from all network devices. + * + * Parameters: + * pDriverObject - pointer to a driver object that represents this driver. + * + * Returns: + * Nothing. + */ + +void +RemoveNetworkHooks(PDRIVER_OBJECT pDriverObject) +{ +// int i; + + //XXX is this necessary? we detach so we should not receive any network IRPs +// if (pDriverObject && pTcpDevice && pTcpDeviceOriginal) +// for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) +// pDriverObject->MajorFunction[i] = pTcpDeviceOriginal->DriverObject->MajorFunction[i]; + + + if (pTcpDeviceOriginal != NULL) + IoDetachDevice(pTcpDeviceOriginal); + + if (pUdpDeviceOriginal != NULL) + IoDetachDevice(pUdpDeviceOriginal); + + if (pIpDeviceOriginal != NULL) + IoDetachDevice(pIpDeviceOriginal); + + + if (pTcpDevice != NULL) + IoDeleteDevice(pTcpDevice); + + if (pUdpDevice != NULL) + IoDeleteDevice(pUdpDevice); + + if (pIpDevice != NULL) + IoDeleteDevice(pIpDevice); +} diff --git a/network.h b/network.h new file mode 100644 index 0000000..012bc8a --- /dev/null +++ b/network.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * network.h + * + * Abstract: + * + * This module defines various types used by the Transport Driver Interface (TDI) network hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 12-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __NETWORK_H__ +#define __NETWORK_H__ + + + +#define NET_DEVICE_TYPE_TCP 1 +#define NET_DEVICE_TYPE_UDP 2 +#define NET_DEVICE_TYPE_IP 3 + + +typedef struct _TDI_CALLBACK +{ + PIO_COMPLETION_ROUTINE Routine; + PVOID Context; + +} TDI_CALLBACK, *PTDI_CALLBACK; + + +typedef int (*TDI_IOCTL_PFUNC) (IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpStack, OUT PTDI_CALLBACK pCompletion, IN ULONG DeviceType); + +typedef struct _TDI_IOCTL +{ + UCHAR MinorFunction; + PCHAR Description; + TDI_IOCTL_PFUNC pfRoutine; + +} TDI_IOCTL, PTDI_IOCTL; + + +BOOLEAN TDIDispatch(PDEVICE_OBJECT pDeviceObject, PIRP pIrp, NTSTATUS *status); +NTSTATUS TDICreate(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpStack, OUT PTDI_CALLBACK pCompletion); +NTSTATUS InstallNetworkHooks(PDRIVER_OBJECT pDriverObject); +void RemoveNetworkHooks(PDRIVER_OBJECT pDriverObject); + + +#endif /* __NETWORK_H__ */ diff --git a/ntproto.h b/ntproto.h new file mode 100644 index 0000000..ddb3d76 --- /dev/null +++ b/ntproto.h @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * ntproto.h + * + * Abstract: + * + * This module defines various types defined in WINNT.H and used by hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 04-Mar-2004 + */ + +#ifndef __NTPROTO_H__ +#define __NTPROTO_H__ + + + +typedef struct _SYSTEM_MODULE_INFORMATION { + ULONG Reserved[2]; + PVOID Base; + ULONG Size; + ULONG Flags; + USHORT Index; + USHORT Unknown; + USHORT LoadCount; + USHORT ModuleNameOffset; + CHAR ImageName[256]; +} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; + + + +/* + * from WINNT.H + */ + +#ifndef _WINNT_ + +typedef unsigned short WORD; +typedef unsigned long DWORD; +typedef unsigned char BYTE; + +#define IMAGE_DOS_SIGNATURE 0x5A4D + +typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header + WORD e_magic; // Magic number + WORD e_cblp; // Bytes on last page of file + WORD e_cp; // Pages in file + WORD e_crlc; // Relocations + WORD e_cparhdr; // Size of header in paragraphs + WORD e_minalloc; // Minimum extra paragraphs needed + WORD e_maxalloc; // Maximum extra paragraphs needed + WORD e_ss; // Initial (relative) SS value + WORD e_sp; // Initial SP value + WORD e_csum; // Checksum + WORD e_ip; // Initial IP value + WORD e_cs; // Initial (relative) CS value + WORD e_lfarlc; // File address of relocation table + WORD e_ovno; // Overlay number + WORD e_res[4]; // Reserved words + WORD e_oemid; // OEM identifier (for e_oeminfo) + WORD e_oeminfo; // OEM information; e_oemid specific + WORD e_res2[10]; // Reserved words + LONG e_lfanew; // File address of new exe header +} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; + + +typedef struct _IMAGE_FILE_HEADER { + WORD Machine; + WORD NumberOfSections; + DWORD TimeDateStamp; + DWORD PointerToSymbolTable; + DWORD NumberOfSymbols; + WORD SizeOfOptionalHeader; + WORD Characteristics; + +} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; + + +typedef struct _IMAGE_DATA_DIRECTORY { + DWORD VirtualAddress; + DWORD Size; + +} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; + + +#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 + +typedef struct _IMAGE_OPTIONAL_HEADER { + // + // Standard fields. + // + + WORD Magic; + BYTE MajorLinkerVersion; + BYTE MinorLinkerVersion; + DWORD SizeOfCode; + DWORD SizeOfInitializedData; + DWORD SizeOfUninitializedData; + DWORD AddressOfEntryPoint; + DWORD BaseOfCode; + DWORD BaseOfData; + + // + // NT additional fields. + // + + DWORD ImageBase; + DWORD SectionAlignment; + DWORD FileAlignment; + WORD MajorOperatingSystemVersion; + WORD MinorOperatingSystemVersion; + WORD MajorImageVersion; + WORD MinorImageVersion; + WORD MajorSubsystemVersion; + WORD MinorSubsystemVersion; + DWORD Win32VersionValue; + DWORD SizeOfImage; + DWORD SizeOfHeaders; + DWORD CheckSum; + WORD Subsystem; + WORD DllCharacteristics; + DWORD SizeOfStackReserve; + DWORD SizeOfStackCommit; + DWORD SizeOfHeapReserve; + DWORD SizeOfHeapCommit; + DWORD LoaderFlags; + DWORD NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + +} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; + + +// "PE\0\0" +#define IMAGE_PE_SIGNATURE 0x00004550 + +typedef struct _IMAGE_NT_HEADERS { + DWORD Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER32 OptionalHeader; + +} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; + + +#ifdef _WIN64 +#error Win64 not supported +#else +typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS; +typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS; +#endif + +typedef struct _IMAGE_EXPORT_DIRECTORY { + DWORD Characteristics; + DWORD TimeDateStamp; + WORD MajorVersion; + WORD MinorVersion; + DWORD Name; + DWORD OrdinalBase; + DWORD NumberOfFunctions; + DWORD NumberOfNames; + DWORD AddressOfFunctions; // RVA from base of image + DWORD AddressOfNames; // RVA from base of image + DWORD AddressOfNameOrdinals; // RVA from base of image + +} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; + + +#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory +#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory +#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory +#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory +#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table +#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory +// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) +#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data +#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP +#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory +#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory +#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers +#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table +#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors +#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor + + +typedef struct _SID_AND_ATTRIBUTES +{ + PSID Sid; + DWORD Attributes; + +} SID_AND_ATTRIBUTES, *PSID_AND_ATTRIBUTES; + + // Query Set +typedef enum _TOKEN_INFORMATION_CLASS +{ + TokenUser = 1, // 1 Y N + TokenGroups, // 2 Y N + TokenPrivileges, // 3 Y N + TokenOwner, // 4 Y Y + TokenPrimaryGroup, // 5 Y Y + TokenDefaultDacl, // 6 Y Y + TokenSource, // 7 Y N + TokenType, // 8 Y N + TokenImpersonationLevel, // 9 Y N + TokenStatistics, // 10 Y N + TokenRestrictedSids, // 11 Y N + TokenSessionId // 12 Y Y + +} TOKEN_INFORMATION_CLASS; + + +/* Information Class 1 */ + +typedef struct _TOKEN_USER +{ + SID_AND_ATTRIBUTES User; + +} TOKEN_USER, *PTOKEN_USER; + + +#define JOB_OBJECT_ASSIGN_PROCESS (0x0001) +#define JOB_OBJECT_SET_ATTRIBUTES (0x0002) +#define JOB_OBJECT_QUERY (0x0004) +#define JOB_OBJECT_TERMINATE (0x0008) +#define JOB_OBJECT_SET_SECURITY_ATTRIBUTES (0x0010) +#define JOB_OBJECT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1F) + +#define MUTANT_QUERY_STATE (0x0001) +#define MUTANT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | MUTANT_QUERY_STATE) + + +#define TIMER_QUERY_STATE (0x0001) +#define TIMER_MODIFY_STATE (0x0002) +#define TIMER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|TIMER_QUERY_STATE|TIMER_MODIFY_STATE) + + +#define PROCESS_TERMINATE (0x0001) +#define PROCESS_CREATE_THREAD (0x0002) +#define PROCESS_SET_SESSIONID (0x0004) +#define PROCESS_VM_OPERATION (0x0008) +#define PROCESS_VM_READ (0x0010) +#define PROCESS_VM_WRITE (0x0020) +#define PROCESS_DUP_HANDLE (0x0040) +#define PROCESS_CREATE_PROCESS (0x0080) +#define PROCESS_SET_QUOTA (0x0100) +#define PROCESS_SET_INFORMATION (0x0200) +#define PROCESS_QUERY_INFORMATION (0x0400) +#define PROCESS_SUSPEND_RESUME (0x0800) +#define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF) + + +#define THREAD_TERMINATE (0x0001) +#define THREAD_SUSPEND_RESUME (0x0002) +#define THREAD_GET_CONTEXT (0x0008) +#define THREAD_SET_CONTEXT (0x0010) +#define THREAD_SET_INFORMATION (0x0020) +#define THREAD_QUERY_INFORMATION (0x0040) +#define THREAD_SET_THREAD_TOKEN (0x0080) +#define THREAD_IMPERSONATE (0x0100) +#define THREAD_DIRECT_IMPERSONATION (0x0200) +#define THREAD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3FF) + + +/* + * Token Specific Access Rights. + */ + +#define TOKEN_ASSIGN_PRIMARY (0x0001) +#define TOKEN_DUPLICATE (0x0002) +#define TOKEN_IMPERSONATE (0x0004) +#define TOKEN_QUERY (0x0008) +#define TOKEN_QUERY_SOURCE (0x0010) +#define TOKEN_ADJUST_PRIVILEGES (0x0020) +#define TOKEN_ADJUST_GROUPS (0x0040) +#define TOKEN_ADJUST_DEFAULT (0x0080) +#define TOKEN_ADJUST_SESSIONID (0x0100) + + +#define CURRENT_THREAD ((HANDLE) -2) +#define CURRENT_PROCESS ((HANDLE) -1) + + +#endif _WINNT_ + + +#endif /* __NTPROTO_H__ */ \ No newline at end of file diff --git a/pathproc.c b/pathproc.c new file mode 100644 index 0000000..8778868 --- /dev/null +++ b/pathproc.c @@ -0,0 +1,1093 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * pathproc.c + * + * Abstract: + * + * This module implements various pathname handling routines. + * + * Author: + * + * Eugene Tsyrklevich 19-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#include "pathproc.h" +#include "procname.h" +#include "policy.h" +#include "learn.h" +#include "log.h" + + +/* + * ResolveFilename() XXX rewrite + * + * Description: + * Get canonical name for a file by resolving symbolic links. + * + * Parameters: + * szFileName - filename to resolve. + * szResult - output buffer. + * szResultSize - size of an output buffer. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +ResolveFilename(IN PCHAR szFileName, OUT PCHAR szResult, IN USHORT szResultSize) +{ + CHAR *p, c; + OBJECT_ATTRIBUTES oa; + ANSI_STRING FileNameAnsi; + UNICODE_STRING FileNameUnicode; + HANDLE hLink; + NTSTATUS rc; + int NumberOfLinks = 0; + WCHAR buffer[chMAX_PATH]; + CHAR buffer2[chMAX_PATH]; + + +restart: + + *szResult = '\0'; + + if (szFileName[0] == '\\' && szFileName[1] == '\\') + szFileName++; + + /* move to the end of the object name */ + for (p = szFileName; *p != '\0'; p++) + ; + + /* process the object name from end to the beginning */ + while (p != szFileName) + { + /* find the last slash */ + if (*p != '\\' && *p != '\0') + { + p--; + continue; + } + + c = *p; + *p = '\0'; + + + RtlInitAnsiString(&FileNameAnsi, szFileName); + RtlAnsiStringToUnicodeString(&FileNameUnicode, &FileNameAnsi, TRUE); + + InitializeObjectAttributes(&oa, &FileNameUnicode, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL); + + rc = ZwOpenSymbolicLinkObject(&hLink, GENERIC_READ, &oa); + + if (NT_SUCCESS(rc)) + { + UNICODE_STRING target; + ANSI_STRING targeta; + + + target.Buffer = buffer; + target.MaximumLength = bMAX_PATH; + target.Length = 0; + + rc = ZwQuerySymbolicLinkObject(hLink, &target, NULL); + + ZwClose(hLink); + + + if (NT_SUCCESS(rc)) + { + targeta.Length = 0; + targeta.MaximumLength = szResultSize; + targeta.Buffer = szResult; + + RtlUnicodeStringToAnsiString(&targeta, &target, FALSE); + targeta.Buffer[targeta.Length] = '\0'; + +//XXX szResultSize -= targeta.Length; + + RtlFreeUnicodeString(&FileNameUnicode); + *p = c; + +//XXX can we have circular links? + if (NumberOfLinks++ < MAX_NUMBER_OF_LINKS) + { + strncat(szResult, p, szResultSize); + +// if (NumberOfLinks > 1) +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("ResolveFilename: NumberOfLinks=%d. Resolved %s to %s. Restarting.\n", NumberOfLinks, szFileName, szResult)); + + /* + * switch szFileName to a different buffer. we cannot reuse szFileName buffer + * since the resolved link might end up being longer than the original buffer + */ + + szFileName = (PCHAR) buffer2; + strcpy(szFileName, szResult); + + goto restart; + } + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("ResolveFilename: NumberOfLinks=%d. bailing out. %s\n", NumberOfLinks, szResult)); + + break; + } + } + + RtlFreeUnicodeString(&FileNameUnicode); + + *p-- = c; + } + + strncat(szResult, p, szResultSize); + + +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("ResolveFilename: name=%s number of links=%d\n", szResult, NumberOfLinks)); + + + return TRUE; +} + + + +BOOLEAN +ResolveFilenameW(IN PUNICODE_STRING szFileName, OUT PCHAR szResult, IN USHORT szResultSize) +{ + WCHAR *p, c; + OBJECT_ATTRIBUTES oa; + ANSI_STRING FileNameAnsi; + UNICODE_STRING FileNameUnicode; + HANDLE hLink; + NTSTATUS rc; + int NumberOfLinks = 0; + WCHAR buffer[chMAX_PATH]; + USHORT OriginalLength; + UNICODE_STRING target; + ANSI_STRING targeta; + + + + ASSERT(szFileName); + ASSERT(szResult); + + OriginalLength = szFileName->Length; + + +restart: + + *szResult = '\0'; + + /* move to the end of the object name */ + p = (PWCHAR) ((PCHAR)szFileName->Buffer + szFileName->Length); + + /* process the object name from end to the beginning */ + while (p != szFileName->Buffer) + { + /* find the last slash */ +// p = wcsrchr(p, L'\\'); +/* + if (p == NULL) + { + p = szFileName->Buffer; + break; + } +*/ + if (*p != L'\\' && *p != L'\0') + { + p--; + continue; + } + + + c = *p; + *p = L'\0'; +// szFileName->Length = OriginalLength - (p - szFileName->Buffer); + + +// RtlInitAnsiString(&FileNameAnsi, szFileName); +// RtlAnsiStringToUnicodeString(&FileNameUnicode, &FileNameAnsi, TRUE); +// InitializeObjectAttributes(&oa, &FileNameUnicode, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL); + + InitializeObjectAttributes(&oa, szFileName, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL); + + rc = ZwOpenSymbolicLinkObject(&hLink, GENERIC_READ, &oa); + + if (! NT_SUCCESS(rc)) + { + *p-- = c; + continue; + } + + target.Buffer = buffer; + target.MaximumLength = bMAX_PATH; + target.Length = 0; + + rc = ZwQuerySymbolicLinkObject(hLink, &target, NULL); + + ZwClose(hLink); + + + if (! NT_SUCCESS(rc)) + { + *p-- = c; + continue; + } + + + *p = c; + + +//XXX can we have circular links? + if (NumberOfLinks++ < MAX_NUMBER_OF_LINKS) + { + wcscat(buffer, p); + RtlInitUnicodeString(szFileName, buffer); + + goto restart; + } + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("ResolveFilename: NumberOfLinks=%d. bailing out. %s\n", NumberOfLinks, szResult)); + + break; + } + + +// wcscat(szResult, p); + + +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("ResolveFilename: name=%s number of links=%d\n", szResult, NumberOfLinks)); + + + return TRUE; +} + + + +/* + * GetPathFromOA() + * + * Description: + * Resolve an object handle to an object name. + * + * Parameters: + * ObjectAttributes - opaque structure describing an object handle. + * OutBuffer - output buffer where an object name will be saved to. + * OutBufferSize - size of an output buffer. + * ResolveLinks - do symbolic links need to be resolved? + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +#define FINISH_GetPathFromOA(msg) \ + do { \ + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, msg); \ + return FALSE; \ + } while(0) + +BOOLEAN +GetPathFromOA(IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PCHAR OutBuffer, IN USHORT OutBufferSize, IN BOOLEAN ResolveLinks) +{ + NTSTATUS rc; + PVOID Object = NULL; + ULONG len; + CHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + bMAX_PATH]; + POBJECT_NAME_INFORMATION pONI = (POBJECT_NAME_INFORMATION) Buffer; + BOOLEAN ret = FALSE; + UNICODE_STRING ObjectName; + + PUNICODE_STRING pusFilename = NULL; + ANSI_STRING name; + + + if (! ARGUMENT_PRESENT(ObjectAttributes) || OutBuffer == NULL) + + return(FALSE); + + + try + { + if (KeGetPreviousMode() != KernelMode) + + ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG)); + + + if (ObjectAttributes->Length != sizeof(OBJECT_ATTRIBUTES)) + + FINISH_GetPathFromOA(("GetPathFromOA: Invalid ObjectAttributes length %d\n", ObjectAttributes->Length)); + + + if (! ARGUMENT_PRESENT(ObjectAttributes->ObjectName) ) + + return FALSE; + + + if (KeGetPreviousMode() != KernelMode) + { + ProbeForRead(ObjectAttributes->ObjectName, sizeof(UNICODE_STRING), sizeof(ULONG)); + + ObjectName = ProbeAndReadUnicodeString(ObjectAttributes->ObjectName); + } + else + { + ObjectName = *ObjectAttributes->ObjectName; + } + + + if (ObjectName.Length == 0) + + return FALSE; + + + if (((ObjectName.Length & (sizeof(WCHAR) - 1)) != 0) || + (ObjectName.Length > bMAX_PATH - sizeof(WCHAR)) ) + + FINISH_GetPathFromOA(("GetPathFromOA: invalid wchar string length = %d\n", ObjectName.Length)); + + + if (KeGetPreviousMode() != KernelMode) + + ProbeForRead(ObjectName.Buffer, ObjectName.Length, sizeof(WCHAR)); + } + + except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("GetPathFromOA(): caught an exception. status = 0x%x\n", status)); + + return FALSE; + } + + + pusFilename = &ObjectName; + + + /* + * is the filename referenced in relation to some directory? + * if so, append the filename to a specified directory name + */ + + if (ARGUMENT_PRESENT(ObjectAttributes->RootDirectory)) + { + if (! NT_SUCCESS( ObReferenceObjectByHandle(ObjectAttributes->RootDirectory, 0, 0, + KernelMode, &Object, NULL) )) + { + FINISH_GetPathFromOA(("GetPathFromOA(): ObReferenceObjectByHandle() failed. Object = %x\n", Object)); + } + + if (Object == NULL) + { + FINISH_GetPathFromOA(("GetPathFromOA(): Object = NULL\n")); + } + + + if (! NT_SUCCESS( ObQueryNameString(Object, pONI, bMAX_PATH, &len) )) + { + ObDereferenceObject(Object); + FINISH_GetPathFromOA(("GetPathFromOA(): ObQueryNameString() failed\n")); + } + + + ObDereferenceObject(Object); + Object = NULL; + + + /* extracted directory name */ + pusFilename = &pONI->Name; + + + /* is the directory name too long? */ + + if (pusFilename->Length >= bMAX_PATH - sizeof(WCHAR)) + FINISH_GetPathFromOA(("GetPathFromOA(): directory name is too long\n")); + + + /* + * pusFilename points to a buffer of MAX_PATH size, ObQueryNameString() sets MaximumLength to the length + * of the directory name, we need to reset this back to MAX_PATH to be able to append a filename + * (reusing the same buffer) + */ + pusFilename->MaximumLength = bMAX_PATH; + + + if (pusFilename->Buffer[ (pusFilename->Length / sizeof(WCHAR)) - 1 ] != L'\\') + { + pusFilename->Buffer[ pusFilename->Length / sizeof(WCHAR) ] = L'\\'; + pusFilename->Length += sizeof(WCHAR); + } + + if (RtlAppendUnicodeStringToString(pusFilename, ObjectAttributes->ObjectName) == STATUS_BUFFER_TOO_SMALL) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("GetPathFromOA: 1 %S\n", pusFilename->Buffer)); + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("GetPathFromOA: 2 %S\n", ObjectAttributes->ObjectName->Buffer)); + FINISH_GetPathFromOA(("GetPathFromOA(): RtlAppendUnicodeStringToString() = STATUS_BUFFER_TOO_SMALL\n")); + } + } + + + if (NT_SUCCESS(RtlUnicodeStringToAnsiString(&name, pusFilename, TRUE))) + { + if (ResolveLinks == TRUE) + { + ret = ResolveFilename(name.Buffer, OutBuffer, OutBufferSize); + } + else + { + if (name.Length >= OutBufferSize - 1) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("GetPathFromOA: Pathname too long %d\n", name.Length)); + + OutBuffer[0] = 0; + + ret = FALSE; + } + else + { + strcpy(OutBuffer, name.Buffer); + + ret = TRUE; + } + } + + RtlFreeAnsiString(&name); + } + + +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("%d GetPathFromOA: %s (%S)\n", (ULONG) PsGetCurrentProcessId(), OutBuffer, pusFilename->Buffer)); + + + return ret; +} + + + + +BOOLEAN +GetPathFromOAW(IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PCHAR OutBuffer, IN USHORT OutBufferSize, IN BOOLEAN ResolveLinks) +{ + NTSTATUS rc; + PVOID Object = NULL; + ULONG len; + CHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + bMAX_PATH]; + POBJECT_NAME_INFORMATION pONI = (POBJECT_NAME_INFORMATION) Buffer; + BOOLEAN ret = FALSE; + UNICODE_STRING ObjectName; + + PUNICODE_STRING pusFilename = NULL; + ANSI_STRING name; + + + if (! ARGUMENT_PRESENT(ObjectAttributes) || OutBuffer == NULL) + + return(FALSE); + + + try + { + if (KeGetPreviousMode() != KernelMode) + + ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG)); + + + if (ObjectAttributes->Length != sizeof(OBJECT_ATTRIBUTES)) + + FINISH_GetPathFromOA(("GetPathFromOA: Invalid ObjectAttributes length %d\n", ObjectAttributes->Length)); + + + if (! ARGUMENT_PRESENT(ObjectAttributes->ObjectName) ) + + return FALSE; + + + if (KeGetPreviousMode() != KernelMode) + { + ProbeForRead(ObjectAttributes->ObjectName, sizeof(UNICODE_STRING), sizeof(ULONG)); + + ObjectName = ProbeAndReadUnicodeString(ObjectAttributes->ObjectName); + } + else + { + ObjectName = *ObjectAttributes->ObjectName; + } + + + if (ObjectName.Length == 0) + + return FALSE; + + + if (((ObjectName.Length & (sizeof(WCHAR) - 1)) != 0) || + (ObjectName.Length > bMAX_PATH - sizeof(WCHAR)) ) + + FINISH_GetPathFromOA(("GetPathFromOA: invalid wchar string length = %d\n", ObjectName.Length)); + + + if (KeGetPreviousMode() != KernelMode) + + ProbeForRead(ObjectName.Buffer, ObjectName.Length, sizeof(WCHAR)); + } + + except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("GetPathFromOA(): caught an exception. status = 0x%x\n", status)); + + return FALSE; + } + + + pusFilename = &ObjectName; + + + /* + * is the filename referenced in relation to some directory? + * if so, append the filename to a specified directory name + */ + + if (ARGUMENT_PRESENT(ObjectAttributes->RootDirectory)) + { + if (! NT_SUCCESS( ObReferenceObjectByHandle(ObjectAttributes->RootDirectory, 0, 0, + KernelMode, &Object, NULL) )) + { + ObDereferenceObject(Object); + FINISH_GetPathFromOA(("GetPathFromOA(): ObReferenceObjectByHandle() failed\n")); + } + + + if (Object == NULL) + { + ObDereferenceObject(Object); + FINISH_GetPathFromOA(("GetPathFromOA(): Object = NULL\n")); + } + + + if (! NT_SUCCESS( ObQueryNameString(Object, pONI, bMAX_PATH, &len) )) + { + ObDereferenceObject(Object); + FINISH_GetPathFromOA(("GetPathFromOA(): ObQueryNameString() failed\n")); + } + + + ObDereferenceObject(Object); + Object = NULL; + + + /* extracted directory name */ + pusFilename = &pONI->Name; + + + /* is the directory name too long? */ + + if (pusFilename->Length >= bMAX_PATH - sizeof(WCHAR)) + FINISH_GetPathFromOA(("GetPathFromOA(): directory name is too long\n")); + + + /* + * pusFilename points to a buffer of MAX_PATH size, ObQueryNameString() sets MaximumLength to the length + * of the directory name, we need to reset this back to MAX_PATH to be able to append a filename + * (reusing the same buffer) + */ + pusFilename->MaximumLength = bMAX_PATH; + + + pusFilename->Buffer[ pusFilename->Length / sizeof(WCHAR) ] = L'\\'; + pusFilename->Length += sizeof(WCHAR); + + + if (RtlAppendUnicodeStringToString(pusFilename, ObjectAttributes->ObjectName) == STATUS_BUFFER_TOO_SMALL) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("GetPathFromOA: 1 %S\n", pusFilename->Buffer)); + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("GetPathFromOA: 2 %S\n", ObjectAttributes->ObjectName->Buffer)); + FINISH_GetPathFromOA(("GetPathFromOA(): RtlAppendUnicodeStringToString() = STATUS_BUFFER_TOO_SMALL\n")); + } + } + + + if (ResolveLinks == TRUE) + { + ret = ResolveFilenameW(pusFilename, OutBuffer, OutBufferSize); + } + +//XXX + if (NT_SUCCESS(RtlUnicodeStringToAnsiString(&name, pusFilename, TRUE))) + { + if (ResolveLinks == TRUE) + { + ret = ResolveFilename(name.Buffer, OutBuffer, OutBufferSize); + } + else + { + if (name.Length >= OutBufferSize - 1) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("GetPathFromOA: Pathname too long %d\n", name.Length)); + + OutBuffer[0] = 0; + + ret = FALSE; + } + else + { + strcpy(OutBuffer, name.Buffer); + + ret = TRUE; + } + } + + RtlFreeAnsiString(&name); + } + + +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("%d GetPathFromOA: %s (%S)\n", (ULONG) PsGetCurrentProcessId(), OutBuffer, pusFilename->Buffer)); + + + return ret; +} + + +/* + * ConvertLongFileNameToShort() + * + * Description: + * Converts long windows filenames to their DOS short equivalent filenames + * (i.e. c:\program files to c:\progra~1). + * + * Parameters: + * LongFileName - long filename buffer. + * ShortFileName - output buffer where a short filename is written to. + * ShortFileNameSize - size of an output buffer (in bytes). + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +#if 0 + +BOOLEAN +ConvertLongFileNameToShort(IN PCHAR LongFileName, OUT PCHAR ShortFileName, IN USHORT ShortFileNameSize) +{ + int LongFileNameIndex = 0, ShortFileNameIndex = 0, CurrentFileNameLength, TotalLength, ExtensionLength, NumberOfSpaces; + BOOLEAN ProcessingExtension = FALSE; + CHAR ch, Extension[3]; + + + if (LongFileName == NULL) + return FALSE; + + TotalLength = strlen(LongFileName); + + + /* if the filename does not start with X:\ then assume \device\blah\ format and skip over the first 2 slashes */ + if (LongFileName[0] == '\\') + { + int Slashes = 0; + + do + { + if ( (ch = ShortFileName[ShortFileNameIndex++] = LongFileName[LongFileNameIndex++]) == '\0') return TRUE; + if (ch == '\\') ++Slashes; + } while (Slashes != 3); + } + + for (NumberOfSpaces = ExtensionLength = CurrentFileNameLength = 0; ; LongFileNameIndex++) + { + /* if we finished traversing the entire directory path or reached a '\' then process the filename (append the extension if necessary) */ + if (LongFileNameIndex == TotalLength || LongFileName[LongFileNameIndex] == '\\') + { + /* + * if the filename is longer than 8 chars or extension is longer than 3 chars then we need + * to create a 6 char filename followed by a '~1' and the first 3 chars of the last extension + */ + + if (CurrentFileNameLength > 8 || ExtensionLength > 3 || NumberOfSpaces > 0) + { + CurrentFileNameLength -= NumberOfSpaces; + + if (CurrentFileNameLength > 7) + { + ShortFileName[ShortFileNameIndex - 2] = '~'; + ShortFileName[ShortFileNameIndex - 1] = '1'; + } + else if (CurrentFileNameLength == 7) + { + ShortFileName[ShortFileNameIndex - 1] = '~'; + ShortFileName[ShortFileNameIndex++] = '1'; + } + else + { + ShortFileName[ShortFileNameIndex++] = '~'; + ShortFileName[ShortFileNameIndex++] = '1'; + } + } + + if (ExtensionLength > 0) + { + ShortFileName[ShortFileNameIndex++] = '.'; + ShortFileName[ShortFileNameIndex++] = Extension[0]; + + if (ExtensionLength > 1) + { + ShortFileName[ShortFileNameIndex++] = Extension[1]; + + if (ExtensionLength > 2) + ShortFileName[ShortFileNameIndex++] = Extension[2]; + } + + ExtensionLength = 0; + ProcessingExtension = FALSE; + } + + /* if we are done traversing the entire path than we can bail */ + if (LongFileNameIndex == TotalLength) + break; + + ShortFileName[ShortFileNameIndex++] = '\\'; + NumberOfSpaces = CurrentFileNameLength = 0; + + continue; + } + + if (LongFileName[LongFileNameIndex] == '.') + { + ProcessingExtension = TRUE; + ExtensionLength = 0; + continue; + } + + if (ProcessingExtension == TRUE) + { + if (ExtensionLength++ < 3) + Extension[ExtensionLength - 1] = LongFileName[LongFileNameIndex]; + + continue; + } + + if (((CurrentFileNameLength++) - NumberOfSpaces) < 8) + { + if (LongFileName[LongFileNameIndex] != ' ') + ShortFileName[ShortFileNameIndex++] = LongFileName[LongFileNameIndex]; + else + ++NumberOfSpaces; + } + } + + + ShortFileName[ShortFileNameIndex++] = 0; + + + return TRUE; +} + +#endif + + + +/* + * GetNameFromHandle() + * + * Description: + * Resolve an object handle to an object name. + * + * Parameters: + * ObjectHandle - handle of an object whose name we are trying to obtain. + * OutBuffer - output buffer where an object name will be saved to. + * OutBufferSize - size of an output buffer (in bytes). + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +PWSTR +GetNameFromHandle(IN HANDLE ObjectHandle, OUT PWSTR OutBuffer, IN USHORT OutBufferSize) +{ + PVOID Object = NULL; + NTSTATUS rc; + POBJECT_NAME_INFORMATION pONI = (POBJECT_NAME_INFORMATION) OutBuffer; + ULONG len; + + + rc = ObReferenceObjectByHandle(ObjectHandle, GENERIC_READ, NULL, KernelMode, &Object, NULL); + if (! NT_SUCCESS(rc)) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("%d GetNameFromHandle: ObReferenceObjectByHandle failed\n", (ULONG) PsGetCurrentProcessId())); + return NULL; + } + + + rc = ObQueryNameString(Object, pONI, OutBufferSize - sizeof(OBJECT_NAME_INFORMATION)*sizeof(WCHAR), &len); + if (! NT_SUCCESS(rc)) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("%d GetNameFromHandle: ObQueryNameString failed\n", (ULONG) PsGetCurrentProcessId())); + return NULL; + } + + +// _snprintf(OutBuffer, OutBufferSize, "%S", pONI->Name.Buffer); +// OutBuffer[OutBufferSize - 1] = 0; + +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("%S (%s)\n", pONI->Name.Buffer, OutBuffer)); + + + ObDereferenceObject(Object); + + + return pONI->Name.Buffer; +// return TRUE; +} + + + +/* + * StripFileMacros() + * + * Description: + * Strip file names of %SystemRoot% and %SystemDrive% macros as well as any specifications. + * + * Parameters: + * Path - ASCII file path to parse. + * Buffer - pointer to an Object where the final result will be saved. + * BufferSize - size of the output Buffer. + * + * Returns: + * Pointer to a stripped ASCII path. + */ + +PCHAR +StripFileMacros(IN PCHAR Path, OUT PCHAR Buffer, IN USHORT BufferSize) +{ + if (_strnicmp(Path, "%systemdrive%:", 14) == 0) + { + return Path + 14; + } + + + if (_strnicmp(Path, "%systemroot%\\", 13) == 0) + { + if (_snprintf(Buffer, MAX_PATH, "%s%s", SystemRootUnresolved, Path + 12) < 0) + return NULL; + + Path = Buffer; + } + + + if (Path[1] == ':' && Path[2] == '\\' && (isalpha(Path[0]) || Path[0] == '?' || Path[0] == '*')) + { + Path += 2; + } + + + return Path; +} + + + +/* + * FixupFilename() + * + * Description: + * Get canonical name for a file (without the drive specification, i.e. \windows\blah.exe) + * + * Parameters: + * szFileName - filename to resolve. + * szResult - output buffer. + * szResultSize - size of an output buffer. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +FixupFilename(IN PCHAR szFileName, OUT PCHAR szResult, IN USHORT szResultSize) +{ + /* skip over \??\ */ + if (_strnicmp(szFileName, "\\??\\", 4) == 0) + { + szFileName += 4; + } + + + /* replace "\SystemRoot" references with the actual system root directory */ + if (_strnicmp(szFileName, "\\SystemRoot\\", 12) == 0) + { + _snprintf(szResult, szResultSize, "%s\\%s", SystemRootDirectory, szFileName + 12); + szResult[ szResultSize - 1 ] = 0; + + return TRUE; + } + + + /* skip over X: drive specifications */ + if (isalpha(szFileName[0]) && szFileName[1] == ':' && szFileName[2] == '\\') + { + szFileName += 2; + } + + + strncpy(szResult, szFileName, szResultSize); + szResult[ szResultSize - 1 ] = 0; + + + return TRUE; +} + + + +/* + * AreMalformedExtensionsAllowed() + * + * Description: + * Check whether the current process is allowed to run binaries with malformed file extensions. + * + * Parameters: + * None. + * + * Returns: + * FALSE if binaries with malformed extensions are not allowed to run. TRUE otherwise. + */ + +BOOLEAN +AreMalformedExtensionsAllowed() +{ + PIMAGE_PID_ENTRY CurrentProcess; + BOOLEAN MalformedExtensionsAllowed = FALSE; + + + /* check the global policy first */ + if (! IS_EXTENSION_PROTECTION_ON(gSecPolicy)) + return TRUE; + + + /* now check the process specific policy */ + CurrentProcess = FindImagePidEntry(CURRENT_PROCESS_PID, 0); + + if (CurrentProcess != NULL) + { + MalformedExtensionsAllowed = ! IS_EXTENSION_PROTECTION_ON(CurrentProcess->SecPolicy); + } + else + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("%d AreMalformedExtensionsAllowed: CurrentProcess = NULL!\n", (ULONG) PsGetCurrentProcessId())); + } + + + return MalformedExtensionsAllowed; +} + + + +/* + * VerifyExecutableName() + * + * Description: + * Make sure the executed binary does not have a funny filename. + * Look out for non-standard extensions (.exe, etc) and double + * extensions that are commonly "ab"used by malware. + * + * Parameters: + * szFileName - filename to verify. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +#define CHECK_LEARNING_MODE() \ + if (LearningMode == TRUE) \ + { \ + TURN_EXTENSION_PROTECTION_OFF(NewPolicy); \ + return TRUE; \ + } + +BOOLEAN +VerifyExecutableName(IN PCHAR szFileName) +{ + SHORT i, len; + BOOLEAN FirstExtension = TRUE; + + + if (LearningMode == FALSE && AreMalformedExtensionsAllowed() == TRUE) + { + /* no need to check anything further, malformed extensions are allowed */ + return TRUE; + } + + + if ((len = (SHORT) strlen(szFileName)) == 0) + return TRUE; + + + for (i = len - 1; i >= 0; i--) + { + /* bail out once we reach the end of the filename */ + if (szFileName[i] == '\\') + break; + + if (szFileName[i] == '.') + { + if (FirstExtension == FALSE) + { + CHECK_LEARNING_MODE(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("VerifyExecutableName: Executing a binary with more than one extension '%s'\n", szFileName)); + + LogAlert(ALERT_SS_PROCESS, OP_PROC_EXECUTE, ALERT_RULE_PROCESS_EXEC_2EXTS, ACTION_LOG, ALERT_PRIORITY_HIGH, NULL, 0, szFileName); + + return FALSE; + } + + if (len - i != 4) + { + CHECK_LEARNING_MODE(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("VerifyExecutableName: Executing a binary with an unknown extension '%s'\n", szFileName)); + + LogAlert(ALERT_SS_PROCESS, OP_PROC_EXECUTE, ALERT_RULE_PROCESS_EXEC_UNKNOWN, ACTION_LOG, ALERT_PRIORITY_HIGH, NULL, 0, szFileName); + + return FALSE; + } + else + { + if (_stricmp(szFileName + i + 1, "exe") != 0 && + _stricmp(szFileName + i + 1, "com") != 0 && + _stricmp(szFileName + i + 1, "bat") != 0 && + _stricmp(szFileName + i + 1, "scr") != 0 && + _stricmp(szFileName + i + 1, "dir") != 0 && + _stricmp(szFileName + i + 1, "tmp") != 0 && + _stricmp(szFileName + i + 1, "_mp") != 0) + { + CHECK_LEARNING_MODE(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("VerifyExecutableName: Executing a binary with an unknown extension '%s'\n", szFileName)); + + LogAlert(ALERT_SS_PROCESS, OP_PROC_EXECUTE, ALERT_RULE_PROCESS_EXEC_UNKNOWN, ACTION_LOG, ALERT_PRIORITY_HIGH, NULL, 0, szFileName); + + return FALSE; + } + } + + FirstExtension = FALSE; + } + } + + + if (FirstExtension == TRUE) + { + CHECK_LEARNING_MODE(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("VerifyExecutableName: Executing binary without an extension '%s'\n", szFileName)); + + LogAlert(ALERT_SS_PROCESS, OP_PROC_EXECUTE, ALERT_RULE_PROCESS_EXEC_NOEXT, ACTION_LOG, ALERT_PRIORITY_HIGH, NULL, 0, szFileName); + + return FALSE; + } + + + return TRUE; +} diff --git a/pathproc.h b/pathproc.h new file mode 100644 index 0000000..9a6c049 --- /dev/null +++ b/pathproc.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * pathproc.h + * + * Abstract: + * + * This module definies various types used by pathname handling routines. + * + * Author: + * + * Eugene Tsyrklevich 19-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __PATHPROC_H__ +#define __PATHPROC_H__ + + +#include +#include "log.h" + + +// some registry keys are actually longer than 260 chars! +// HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\DeviceClasses\{6994AD04-93EF-11D0-A3CC-00A0C9223196}\##?#Root#SYSTEM#0000#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\#{2f412ab5-ed3a-4590-ab24-b0ce2aa77d3c}&{9B365890-165F-11D0-A195-0020AFD156E4}\Device Parameters +//#define MAX_PATH 260 + +#define MAX_PATH 300 +#define chMAX_PATH MAX_PATH +#define bMAX_PATH (MAX_PATH * sizeof(WCHAR)) + + +/* maximum number of links to follow */ +#define MAX_NUMBER_OF_LINKS 3 + + +#define DO_NOT_RESOLVE_LINKS 0 +#define RESOLVE_LINKS 1 + + +BOOLEAN GetPathFromOA(IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PCHAR OutBuffer, IN USHORT OutBufferSize, IN BOOLEAN ResolveLinks); +BOOLEAN GetPathFromOAW(IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PCHAR OutBuffer, IN USHORT OutBufferSize, IN BOOLEAN ResolveLinks); +BOOLEAN ResolveFilename(IN PCHAR szFileName, OUT PCHAR szResult, IN USHORT szResultSize); +BOOLEAN ResolveFilenameW(IN PUNICODE_STRING szFileName, OUT PCHAR szResult, IN USHORT szResultSize); +PWSTR GetNameFromHandle(IN HANDLE ObjectHandle, OUT PWSTR OutBuffer, IN USHORT OutBufferSize); +BOOLEAN FixupFilename(IN PCHAR szFileName, OUT PCHAR szResult, IN USHORT szResultSize); +BOOLEAN VerifyExecutableName(IN PCHAR szFileName); +PCHAR StripFileMacros(IN PCHAR Path, OUT PCHAR Buffer, IN USHORT BufferSize); +BOOLEAN ConvertLongFileNameToShort(IN PCHAR LongFileName, OUT PCHAR ShortFileName, IN USHORT ShortFileNameSize); + + +NTSTATUS +ObQueryNameString( + IN PVOID Object, + OUT POBJECT_NAME_INFORMATION ObjectNameInfo, + IN ULONG Length, + OUT PULONG ReturnLength + ); + + +#endif /* __PATHPROC_H__ */ \ No newline at end of file diff --git a/policy.c b/policy.c new file mode 100644 index 0000000..be08a81 --- /dev/null +++ b/policy.c @@ -0,0 +1,3243 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * policy.c + * + * Abstract: + * + * This module implements various security policy parsing and enforcement routines. + * + * Author: + * + * Eugene Tsyrklevich 16-Feb-2004 + * + * Revision History: + * + * None. + */ + +// XXX rename all funcs as SpYYY ? (same for other modules?) + +#include +#include "policy.h" +#include "pathproc.h" +#include "procname.h" +#include "hookproc.h" +#include "media.h" +#include "learn.h" +#include "misc.h" +#include "i386.h" + + +#include "process.h" +#include "log.h" + + +BOOLEAN PolicyParseRule(OUT PSECURITY_POLICY pSecPolicy, IN PCHAR rule, OUT BOOLEAN *Critical); +BOOLEAN PolicyParsePolicyRule(OUT PSECURITY_POLICY pSecPolicy, IN PCHAR Operation, IN PCHAR rule, OUT BOOLEAN *Critical); +BOOLEAN PolicyParseObjectRule(PSECURITY_POLICY pSecPolicy, RULE_TYPE RuleType, PCHAR Operation, PCHAR rule); +BOOLEAN PolicyParseSyscallRule(PSECURITY_POLICY pSecPolicy, PCHAR SyscallName, PCHAR rule); +BOOLEAN PolicyParseProtectionRule(PSECURITY_POLICY pSecPolicy, PCHAR Operation, PCHAR rule); +BOOLEAN PolicyParseMediaRule(PSECURITY_POLICY pSecPolicy, PCHAR Operation, PCHAR rule); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitPolicy) +#pragma alloc_text (PAGE, PolicyPostBootup) +#pragma alloc_text (PAGE, PolicyRemove) +#endif + + +/* + * example: + * + * SystemRoot - \device\harddisk1\windows + * SystemRootUnresolved - c:\windows + * SystemRootDirectory - \windows + * CDrive - \device\harddisk1 + */ + +CHAR SystemDrive, SystemRoot[MAX_PATH], SystemRootUnresolved[MAX_PATH], *SystemRootDirectory, CDrive[MAX_PATH]; +USHORT SystemRootLength = 0, SystemRootUnresolvedLength = 0, SystemRootDirectoryLength = 0, CDriveLength = 0; + +USHORT gPolicyLineNumber; +PWSTR gPolicyFilename, gFilePath; + +// to be portable on 32 & 64 bit platforms +ULONG NumberOfBitsInUlong, UlongBitShift; + +/* LoadPolicy() can be used by only one thread at a time due to use of global variables */ +KMUTEX LoadPolicyMutex; + +/* Global Security Policy */ +SECURITY_POLICY gSecPolicy; + + + +/* + * InitPolicy() + * + * Description: + * Initialize the policy engine. Load the global policy. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE if everything is OK, FALSE if failed. + */ + +BOOLEAN +InitPolicy() +{ + NumberOfBitsInUlong = sizeof(ULONG) * 8; + UlongBitShift = sizeof(ULONG) == 4 ? 5 : 6; + + + /* gets reinitialized correctly once bootup is complete (see PolicyPostBootup) */ + SystemDrive = 'C'; + + if (ReadSymlinkValue(L"\\SystemRoot", SystemRootUnresolved, MAX_PATH) == FALSE) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("InitPolicy: ReadSymlinkValue failed\n")); + return FALSE; + } + + SystemRootUnresolvedLength = (USHORT) strlen(SystemRootUnresolved); + + ResolveFilename(SystemRootUnresolved, SystemRoot, MAX_PATH); + + + /* extract the system directory name by itself (i.e. \windows) */ + SystemRootDirectory = strrchr(SystemRoot, '\\'); + + if (SystemRootDirectory == NULL) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("InitPolicy: SystemRootDirectory is NULL\n")); + return FALSE; + } + + SystemRootDirectoryLength = (USHORT) strlen(SystemRootDirectory); + + + if (ReadSymlinkValue(L"\\??\\C:", CDrive, MAX_PATH) == FALSE) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("InitPolicy: Failed to open C: symbolic link\n")); + return FALSE; + } + + CDriveLength = (USHORT) strlen(CDrive); + + + if (PolicyPostBootup() == FALSE) + { + /* + * if boot process is not complete yet then we cannot get SystemRootUnresolved (i.e. c:\windows) + * because parts of registry are not initialized yet (see PolicyPostBootup) + * + * In that case, try to assemble SystemRootUnresolved manually + */ + + SystemRootUnresolved[0] = SystemDrive; + SystemRootUnresolved[1] = ':'; + + strcpy(SystemRootUnresolved + 2, SystemRootDirectory); + } + + + LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("InitPolicy: SystemRoot=%s (%s, %s)\n", SystemRoot, SystemRootUnresolved, SystemRootDirectory)); + + + KeInitializeMutex(&LoadPolicyMutex, 0); + + RtlZeroMemory(&gSecPolicy, sizeof(gSecPolicy)); + + KeInitializeSpinLock(&gSecPolicy.SpinLock); + + + if (LearningMode == TRUE) + + return TRUE; + + + if (FindAndLoadSecurityPolicy(&gSecPolicy, L"computer", NULL) == FALSE) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_WARNING, ("InitPolicy: LoadSecurityPolicy(computer.policy) failed\n")); + gSecPolicy.DefaultPolicyAction = ACTION_PERMIT_DEFAULT; + } + + + if (gSecPolicy.DefaultPolicyAction != ACTION_PERMIT_DEFAULT) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_WARNING, ("InitPolicy: Global policy default action must be permit\n")); + gSecPolicy.DefaultPolicyAction = ACTION_PERMIT_DEFAULT; + } + + + return TRUE; +} + + + +/* + * PolicyPostBootup() + * + * Description: + * Finish initializing system variables once the bootup process is complete. + * We are unable to read the SystemRoot registry value before the bootup is complete since + * that part of the registry has not been initialized yet. + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +PolicyPostBootup() +{ + ASSERT(BootingUp == FALSE); + + + if (ReadStringRegistryValueA(L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion", L"SystemRoot", SystemRootUnresolved, MAX_PATH) == FALSE) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("PolicyPostBootup: Failed to open SystemRoot registry key\n")); + return FALSE; + } + + SystemRootUnresolvedLength = (USHORT) strlen(SystemRootUnresolved); + + SystemDrive = (CHAR) toupper(SystemRootUnresolved[0]); + + + return TRUE; +} + + + +/* + * PolicyRemove() + * + * Description: + * Shutdown the policy engine. Delete the global policy + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +void +PolicyRemove() +{ + PolicyDelete(&gSecPolicy); +} + + + +/* + * PolicyDelete() + * + * Description: + * Delete a security policy. Free all the rules associated with a policy. + * + * Parameters: + * pSecPolicy - pointer to a security policy to delete. + * + * Returns: + * Nothing. + */ + +void +PolicyDelete(IN PSECURITY_POLICY pSecPolicy) +{ + PPOLICY_RULE r, tmp; + KIRQL irql; + UCHAR i; + + + if (pSecPolicy == NULL) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("PolicyDelete: pSecPolicy is NULL\n")); + return; + } + + + if (pSecPolicy->Initialized == FALSE) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("PolicyDelete: pSecPolicy is not initialized\n")); + return; + } + + + KeAcquireSpinLock(&pSecPolicy->SpinLock, &irql); + + for (i = 0; i < RULE_LASTONE; i++) + { + r = pSecPolicy->RuleList[i]; + + while (r) + { + tmp = r; + r = (PPOLICY_RULE) r->Next; + + ExFreePoolWithTag(tmp, _POOL_TAG); + } + } + + if (pSecPolicy->Name) + { + ExFreePoolWithTag(pSecPolicy->Name, _POOL_TAG); + pSecPolicy->Name = NULL; + } + + pSecPolicy->Initialized = FALSE; + + RtlZeroMemory(pSecPolicy->RuleList, sizeof(pSecPolicy->RuleList)); + + + KeReleaseSpinLock(&pSecPolicy->SpinLock, irql); +} + + + +/* + * LoadSecurityPolicy() + * + * Description: + * Parses and loads a security policy. + * + * Parameters: + * pSecPolicy - pointer to a security policy to initialize. + * PolicyFile - string containing the policy filename to parse + * FilePath - string containing full program path of the file we are loading policy for + * + * Returns: + * TRUE if security policy was successfully parsed and loaded, FALSE otherwise. + */ + +BOOLEAN +LoadSecurityPolicy(OUT PSECURITY_POLICY pSecPolicy, IN PWSTR PolicyFile, IN PWSTR FilePath) +{ + OBJECT_ATTRIBUTES oa; + HANDLE hFile = 0; + UNICODE_STRING usPolicyFile; + ULONG size; + NTSTATUS status; + IO_STATUS_BLOCK isb; + CHAR *p, buffer[POLICY_MAX_RULE_LENGTH]; + INT64 offset; + BOOLEAN ret = TRUE, Critical = FALSE; + + + if (pSecPolicy == NULL || PolicyFile == NULL) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("LoadSecurityPolicy(%x, %x, %x): NULL parameter\n", pSecPolicy, PolicyFile, FilePath)); + return FALSE; + } + + + pSecPolicy->Initialized = FALSE; + pSecPolicy->DefaultPolicyAction = ACTION_NONE; + pSecPolicy->ProtectionFlags = BootingUp ? PROTECTION_ALL_OFF : PROTECTION_ALL_ON; + + + RtlInitUnicodeString(&usPolicyFile, PolicyFile); + + LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("LoadSecurityPolicy: Parsing %S\n", usPolicyFile.Buffer)); + + + InitializeObjectAttributes(&oa, &usPolicyFile, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + if (!NT_SUCCESS(ZwCreateFile(&hFile, GENERIC_READ, &oa, &isb, + NULL, 0, FILE_SHARE_READ, FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0))) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("LoadSecurityPolicy: Failed to open file %S\n", usPolicyFile.Buffer)); + return FALSE; + } + + + offset = 0; + buffer[0] = 0; + + + /* only one thread at a time can use LoadPolicyMutex due to use of global variables (PolicyLineNumber, buffer) */ + KeWaitForMutexObject(&LoadPolicyMutex, Executive, KernelMode, FALSE, NULL); + + + gPolicyLineNumber = 1; + gPolicyFilename = usPolicyFile.Buffer; + gFilePath = FilePath; + + while (1) + { + status = ZwReadFile(hFile, NULL, NULL, NULL, &isb, (PVOID) buffer, sizeof(buffer) - 1, + (PLARGE_INTEGER) &offset, NULL); + + if (! NT_SUCCESS(status)) + { + if (status != STATUS_END_OF_FILE) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("LoadSecurityPolicy: ZwReadFile failed rc=%x\n", status)); + ret = FALSE; + PolicyDelete(pSecPolicy); + } + + break; + } + + if (isb.Information == 0) + break; + + buffer[isb.Information] = '\0'; + + /* + * strchr() will return NULL when the line we read exceeds the size of the buffer or + * the last line was not '\n' terminated + */ + + if ((p = strchr(buffer, '\n')) == NULL) + { + /* don't try to parse very long lines */ + + if (isb.Information == sizeof(buffer) - 1) + { + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("LoadSecurityPolicy(%s:%d): Rule is too long\n", gPolicyFilename, gPolicyLineNumber)); + + PolicyDelete(pSecPolicy); + + ret = FALSE; + break; + } + + + /* the last rule was not '\n' terminated */ + + if (PolicyParseRule(pSecPolicy, buffer, &Critical) == FALSE) + { + if (Critical == TRUE) + { + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_DEBUG, ("LoadSecurityPolicy(%S:%d): Encountered a critical error. Aborting.\n", gPolicyFilename, gPolicyLineNumber)); + + PolicyDelete(pSecPolicy); + + ret = FALSE; + break; + } + } + + ret = TRUE; + + break; + } + + *p = 0; + + if (p != buffer && *(p - 1) == '\r') + *(p - 1) = 0; + + + if (PolicyParseRule(pSecPolicy, buffer, &Critical) == FALSE) + { + if (Critical == TRUE) + { + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_DEBUG, ("LoadSecurityPolicy(%S:%d): Encountered a critical error. Aborting.\n", gPolicyFilename, gPolicyLineNumber)); + + PolicyDelete(pSecPolicy); + + ret = FALSE; + break; + } + } + + + offset += p - buffer + 1; + + + if (++gPolicyLineNumber > 10000) + { + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("LoadSecurityPolicy: Policy '%S' is too long. Maximum number of lines is 10000.\n", gPolicyFilename)); + + PolicyDelete(pSecPolicy); + + ret = FALSE; + break; + } + } + + ZwClose(hFile); + + + if (ret != FALSE) + { + pSecPolicy->Initialized = TRUE; + + if (pSecPolicy->DefaultPolicyAction == ACTION_NONE) + pSecPolicy->DefaultPolicyAction = DEFAULT_POLICY_ACTION; + } + + + LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("LoadSecurityPolicy: Done Parsing %S. Total number of lines %d. (ret=%d)\n", usPolicyFile.Buffer, gPolicyLineNumber, ret)); + + + KeReleaseMutex(&LoadPolicyMutex, FALSE); + + + return ret; +} + + + +/* + * FindAndLoadSecurityPolicy() + * + * Description: + * Finds and loads a security policy associated with a specified executable filename. + * + * Parameters: + * pSecPolicy - pointer to a security policy to initialize. + * FilePath - string specifying the complete path to an executable + * UserName - optional username, if specified check for a policy in "username" directory first + * + * Returns: + * TRUE if security policy was successfully parsed and loaded, FALSE otherwise. + */ + +BOOLEAN +FindAndLoadSecurityPolicy(OUT PSECURITY_POLICY pSecPolicy, IN PWSTR FilePath, IN PWSTR UserName) +{ + PWSTR filename; + WCHAR PolicyPath[MAX_PATH]; + BOOLEAN ret; + int len; + + + if (pSecPolicy == NULL || FilePath == NULL) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("FindAndLoadSecurityPolicy: NULL argument %x %x\n", pSecPolicy, FilePath)); + return FALSE; + } + + + if (KeGetCurrentIrql() != 0) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("FindAndLoadSecurityPolicy(): irql=%d\n", KeGetCurrentIrql())); + return FALSE; + } + + + filename = wcsrchr(FilePath, L'\\'); + + if (filename == NULL) + filename = FilePath; + else + ++filename; + + + /* if user policy load fails, we loop here again to load the global policy */ +ReloadPolicy: + + if (UserName != NULL) + _snwprintf(PolicyPath, MAX_PATH, L"\\??\\%s\\policy\\%s\\%s.policy", OzoneInstallPath, UserName, filename); + else + _snwprintf(PolicyPath, MAX_PATH, L"\\??\\%s\\policy\\%s.policy", OzoneInstallPath, filename); + + PolicyPath[MAX_PATH - 1] = 0; + + + LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("FindAndLoadSecurityPolicy: Loading policy for %S (%S)\n", PolicyPath, FilePath)); + + + ret = LoadSecurityPolicy(pSecPolicy, PolicyPath, FilePath); + if (ret == FALSE) + { + /* If we can't find a policy specific to a user, try to load a global policy instead */ + if (UserName != NULL) + { + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("FindAndLoadSecurityPolicy: Cannot find '%S' policy for user '%S'. Looking for a global policy..\n", filename, UserName)); + + UserName = NULL; + + goto ReloadPolicy; + } + + return FALSE; + } + + + /* allocate extra space for ".policy" string */ + len = wcslen(filename) + 7 + 1; + + pSecPolicy->Name = ExAllocatePoolWithTag(NonPagedPool, len * sizeof(WCHAR), _POOL_TAG); + + if (pSecPolicy->Name != NULL) + { + _snwprintf(pSecPolicy->Name, len, L"%s.policy", filename); + } + else + { + PolicyDelete(pSecPolicy); + ret = FALSE; + } + + return ret; +} + + + +/* + * PolicyParseRule() + * + * Description: + * Parses a specified rule. + * + * Parameters: + * pSecPolicy - pointer to a security policy that will contain the parsed rule. + * rule - string buffer containing a rule to parse. + * Critical - Boolean indicating whether the parser should abort parsing the policy due to a critical error. + * + * Returns: + * TRUE if the policy rule was successfully parsed and loaded, FALSE otherwise. + */ + +#define SKIP_WHITESPACE(str) do { while(*(str) == ' ' || *(str) == '\t') ++(str); } while(0) +#define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\t') + +/* macro shortcut for bailing out of PolicyParseRule() in case of an error */ + +#define ABORT_PolicyParseRule(msg) \ + do { \ + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Encountered an error while parsing %S:%d :\n", gPolicyFilename, gPolicyLineNumber)); \ + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, msg); \ + return FALSE; \ + } while (0) + + +static BOOLEAN +PolicyParseRule(OUT PSECURITY_POLICY pSecPolicy, IN PCHAR rule, OUT BOOLEAN *Critical) +{ + CHAR ServiceName[POLICY_MAX_SERVICE_NAME_LENGTH]; + PCHAR OriginalRule = rule; + int i = 0, SawSpace = 0, SawUnderscore = 0; + + + *Critical = FALSE; + + + SKIP_WHITESPACE(rule); + + + /* skip empty lines */ + + if (*rule == 0) + return TRUE; + + + /* comments start with '#' */ + + if (*rule == '#') + return TRUE; + + + /* + * Parse the service name. Format: "ServiceName:" or "ServiceName_OperationType:" + * where ServiceName can be "file", "registry", "event", "memory", etc + * and OperationType can be "read", "write", "rw" + */ + + while (*rule != 0 && *rule != ':') + { + /* is the specified syscall name too long? */ + + if (i > POLICY_MAX_SERVICE_NAME_LENGTH - 1) + ABORT_PolicyParseRule(("Rule type specification is too long. Maximum rule type specification length is %d characters.\n", POLICY_MAX_SERVICE_NAME_LENGTH)); + + + /* allow whitespace before the colon */ + + if (IS_WHITESPACE(*rule)) + { + ++rule; + + SawSpace = 1; + + continue; + } + + + /* Service Names are not allowed to contain a space */ + + if (SawSpace) + ABORT_PolicyParseRule(("Rule type specification cannot contain a space\n")); + + + /* Expecting to see 1 underscore '_' */ + + if (*rule == '_') + { + /* There can be only be 1 underscore char. and it cannot be the first char. */ + if (i == 0 || SawUnderscore) + ABORT_PolicyParseRule(("Rule type specification cannot contain multiple underscore characters\n")); + + /* remember the underscore position */ + SawUnderscore = i; + } + + + ServiceName[i++] = *rule++; + } + + + /* Expecting to have read more than 1 character, finishing with a ':' */ + if (i == 0 || *rule++ != ':') + ABORT_PolicyParseRule(("Colon not found. Rule type specification must end with a colon ':'.\n")); + + + ServiceName[i] = 0; + + + SKIP_WHITESPACE(rule); + + + /* didn't see any underscores. assume "system_call_name:" format */ + + if (SawUnderscore == 0) + ABORT_PolicyParseRule(("Underscore not found. Rule type specification must contain an underscore.\n")); + + + ServiceName[SawUnderscore] = 0; + + + // file operation + if (strlen(ServiceName) == 4 && _stricmp(ServiceName, "file") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_FILE, ServiceName + SawUnderscore + 1, rule); + + // directory operation + if (strlen(ServiceName) == 9 && _stricmp(ServiceName, "directory") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_DIRECTORY, ServiceName + SawUnderscore + 1, rule); + + // registry operation + if (strlen(ServiceName) == 8 && _stricmp(ServiceName, "registry") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_REGISTRY, ServiceName + SawUnderscore + 1, rule); + + // memory section operation + if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "section") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_SECTION, ServiceName + SawUnderscore + 1, rule); + + // memory section / dll operation + if (strlen(ServiceName) == 3 && _stricmp(ServiceName, "dll") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_DLL, ServiceName + SawUnderscore + 1, rule); + + // event operation + if (strlen(ServiceName) == 5 && _stricmp(ServiceName, "event") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_EVENT, ServiceName + SawUnderscore + 1, rule); + + // semaphore operation + if (strlen(ServiceName) == 9 && _stricmp(ServiceName, "semaphore") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_SEMAPHORE, ServiceName + SawUnderscore + 1, rule); + + // mailslot operation + if (strlen(ServiceName) == 8 && _stricmp(ServiceName, "mailslot") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_MAILSLOT, ServiceName + SawUnderscore + 1, rule); + + // named pipe operation + if (strlen(ServiceName) == 9 && _stricmp(ServiceName, "namedpipe") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_NAMEDPIPE, ServiceName + SawUnderscore + 1, rule); + + // job object operation + if (strlen(ServiceName) == 3 && _stricmp(ServiceName, "job") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_JOB, ServiceName + SawUnderscore + 1, rule); + + // mutant operation + if (strlen(ServiceName) == 5 && _stricmp(ServiceName, "mutex") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_MUTANT, ServiceName + SawUnderscore + 1, rule); + + // port operation + if (strlen(ServiceName) == 4 && _stricmp(ServiceName, "port") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_PORT, ServiceName + SawUnderscore + 1, rule); + + // symlink operation + if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "symlink") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_SYMLINK, ServiceName + SawUnderscore + 1, rule); + + // timer operation + if (strlen(ServiceName) == 5 && _stricmp(ServiceName, "timer") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_TIMER, ServiceName + SawUnderscore + 1, rule); + + // process operation + if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "process") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_PROCESS, ServiceName + SawUnderscore + 1, rule); + + // driver operation + if (strlen(ServiceName) == 6 && _stricmp(ServiceName, "driver") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_DRIVER, ServiceName + SawUnderscore + 1, rule); + + // object directory operation + if (strlen(ServiceName) == 6 && _stricmp(ServiceName, "dirobj") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_DIROBJ, ServiceName + SawUnderscore + 1, rule); + + // atom operation + if (strlen(ServiceName) == 4 && _stricmp(ServiceName, "atom") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_ATOM, ServiceName + SawUnderscore + 1, rule); + + // network operation + if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "network") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_NETWORK, ServiceName + SawUnderscore + 1, rule); + + // service operation + if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "service") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_SERVICE, ServiceName + SawUnderscore + 1, rule); + + // time operation + if (strlen(ServiceName) == 4 && _stricmp(ServiceName, "time") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_TIME, ServiceName + SawUnderscore + 1, rule); + + // token operation + if (strlen(ServiceName) == 5 && _stricmp(ServiceName, "token") == 0) + return PolicyParseObjectRule(pSecPolicy, RULE_TOKEN, ServiceName + SawUnderscore + 1, rule); + + + /* + * non object rules + */ + + // syscall + if (strlen(ServiceName) == 7 && _stricmp(ServiceName, "syscall") == 0) + return PolicyParseSyscallRule(pSecPolicy, ServiceName + SawUnderscore + 1, rule); + + // policy + if (strlen(ServiceName) == 6 && _stricmp(ServiceName, "policy") == 0) + return PolicyParsePolicyRule(pSecPolicy, ServiceName + SawUnderscore + 1, rule, Critical); + + // protection + if (strlen(ServiceName) == 10 && _stricmp(ServiceName, "protection") == 0) + return PolicyParseProtectionRule(pSecPolicy, ServiceName + SawUnderscore + 1, rule); + + // media + if (strlen(ServiceName) == 5 && _stricmp(ServiceName, "media") == 0) + return PolicyParseMediaRule(pSecPolicy, ServiceName + SawUnderscore + 1, rule); + + + + ABORT_PolicyParseRule(("Invalid rule type specification: '%s'.\n", ServiceName)); +} + + + +/* + * ParseDllOperation() + * + * Description: + * Parses an operation (i.e. "load" in "dll_load") specified for a DLL object rule. + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParseDllOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 4 && _stricmp(Operation, "load") == 0) + return OP_LOAD; + + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + return OP_INVALID; +} + + + +/* + * ParseTimeOperation() + * + * Description: + * Parses an operation (i.e. "change" in "time_change") specified for a Time object rule. + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParseTimeOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 6 && _stricmp(Operation, "change") == 0) + return OP_LOAD; + + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + return OP_INVALID; +} + + + +/* + * ParseTokenOperation() + * + * Description: + * Parses an operation (i.e. "modify" in "token_modify") specified for a Token object rule. + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParseTokenOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 6 && _stricmp(Operation, "modify") == 0) + return OP_TOKEN_MODIFY; + + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + return OP_INVALID; +} + + + +/* + * ParsePortOperation() + * + * Description: + * Parses an operation (i.e. "create" in "port_create") specified for a port object rule. + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParsePortOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 6 && _stricmp(Operation, "create") == 0) + return OP_PORT_CREATE; + + if (strlen(Operation) == 7 && _stricmp(Operation, "connect") == 0) + return OP_PORT_CONNECT; + + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + + return OP_INVALID; +} + + + +/* + * ParseCreateOpenOperation() + * + * Description: + * Parses a create or an open operation (i.e. "create" in "dirobj_create"). + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParseCreateOpenOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 6 && _stricmp(Operation, "create") == 0) + return OP_CREATE; + + if (strlen(Operation) == 4 && _stricmp(Operation, "open") == 0) + return OP_OPEN; + + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + + return OP_INVALID; +} + + + +/* + * ParseAtomOperation() + * + * Description: + * Parses an operation (i.e. "find" in "atom_find") specified for an atom object rule. + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParseAtomOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 4 && _stricmp(Operation, "find") == 0) + return OP_FIND; + + if (strlen(Operation) == 3 && _stricmp(Operation, "add") == 0) + return OP_ADD; + + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + + return OP_INVALID; +} + + + +/* + * ParseDriverOperation() + * + * Description: + * Parses an operation (i.e. "load" in "driver_load") specified for a driver object rule. + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParseDriverOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 4 && _stricmp(Operation, "load") == 0) + return OP_LOAD; + + if (strlen(Operation) == 7 && _stricmp(Operation, "regload") == 0) + return OP_REGLOAD; + + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + return OP_INVALID; +} + + + +/* + * ParseDirectoryOperation() + * + * Description: + * Parses an operation (i.e. "create" in "directory_create") specified for a directory object rule. + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParseDirectoryOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 6 && _stricmp(Operation, "create") == 0) + return OP_DIR_CREATE; + + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + return OP_INVALID; +} + + + +/* + * ParseObjectOperation() + * + * Description: + * Parses an operation (i.e. "read" in "file_read") specified for an object (file, registry, etc) rule. + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParseObjectOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 4 && _stricmp(Operation, "read") == 0) + return OP_READ; + + if (strlen(Operation) == 5 && _stricmp(Operation, "write") == 0) + return OP_WRITE; + + if (strlen(Operation) == 2 && _stricmp(Operation, "rw") == 0) + return (OP_READ | OP_WRITE); + + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + //XXX valid for files only +// if (strlen(Operation) == 6 && _stricmp(Operation, "append") == 0) +// return OP_APPEND; + + if (strlen(Operation) == 7 && _stricmp(Operation, "execute") == 0) + return OP_EXECUTE; + + if (strlen(Operation) == 6 && _stricmp(Operation, "delete") == 0) + return OP_DELETE; + + + return OP_INVALID; +} + + + +/* + * ParseProcessOperation() + * + * Description: + * Parses an operation (i.e. "execute" in "process_execute") specified for a process rule. + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParseProcessOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + if (strlen(Operation) == 7 && _stricmp(Operation, "execute") == 0) + return OP_PROC_EXECUTE; + + if (strlen(Operation) == 4 && _stricmp(Operation, "open") == 0) + return OP_PROC_OPEN; + + + return OP_INVALID; +} + + + +/* + * ParseServiceOperation() + * + * Description: + * Parses an operation (i.e. "start" in "service_start") specified for a service object rule. + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParseServiceOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + /* + * We cannot distinguish between various service operations since service rules are actually + * enforced by the registry rules. Thus convert all operations to OP_ALL for now. + */ + + if (strlen(Operation) == 5 && _stricmp(Operation, "start") == 0) + return OP_ALL;//OP_SERVICE_START; + + if (strlen(Operation) == 4 && _stricmp(Operation, "stop") == 0) + return OP_ALL;//OP_SERVICE_STOP; + + if (strlen(Operation) == 6 && _stricmp(Operation, "create") == 0) + return OP_ALL;//OP_SERVICE_CREATE; + + if (strlen(Operation) == 6 && _stricmp(Operation, "delete") == 0) + return OP_ALL;//OP_SERVICE_DELETE; + + return OP_INVALID; +} + + + +/* + * ParseNetworkOperation() + * + * Description: + * Parses an operation (i.e. "bind" in "network_bind") specified for a network object rule. + * + * Parameters: + * Operation - specified operation. + * + * Returns: + * OP_INVALID if a specified operation is invalid or an OP_* value corresponding to the parsed operation. + */ + +UCHAR +ParseNetworkOperation(IN PCHAR Operation) +{ + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + return OP_ALL; + + if (strlen(Operation) == 10 && _stricmp(Operation, "tcpconnect") == 0) + return OP_TCPCONNECT; + + if (strlen(Operation) == 10 && _stricmp(Operation, "udpconnect") == 0) + return OP_UDPCONNECT; + + if (strlen(Operation) == 7 && _stricmp(Operation, "connect") == 0) + return OP_CONNECT; + + if (strlen(Operation) == 4 && _stricmp(Operation, "bind") == 0) + return OP_BIND; + + return OP_INVALID; +} + + + + +/*****************************************************************************/ + + + + +/* + * ParseNetworkObject() + * + * Description: + * Parses the specified network address (i.e. "127.0.0.1:443"). + * + * Parameters: + * name - string value to parse. + * Object - pointer to an Object where the final result will be saved + * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions. + * + * Returns: + * INVALID_OBJECT_SIZE if the specified network address is invalid. 0 to indicate SUCCESS. + * Network addresses do not require any additional memory to be allocated thus the returned size is 0. + */ + +size_t +ParseNetworkObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard) +{ + PCHAR colon; + + + //XXX + // for now connect format is "ipaddr" while bind format is "ipaddr:port" + + colon = strchr(name, ':'); + + if (colon) + { + *Object = colon + 1; +// if ((*Object = (PVOID) atoi(colon + 1)) == 0) +// return INVALID_OBJECT_SIZE; + } + else + { + *Object = name; +// if ((*Object = (PVOID) inet_addr(name)) == 0) +// return INVALID_OBJECT_SIZE; + } + + + return strlen(*Object); +} + + + +/* + * ParseStub() + * + * Description: + * Parse stub for strings that do no require any further parsing. + * + * Parameters: + * name - string value to parse. + * Object - pointer to an Object where the final result will be saved. + * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions. + * + * Returns: + * Length of the specified string value. + */ + +size_t +ParseStub(IN PCHAR name, OUT PCHAR *ObjectName, OUT BOOLEAN *wildcard) +{ + *ObjectName = name; + + return strlen(name); +} + + + +/* + * ParseRegistryObject() + * + * Description: + * Convert user land registry object names into their kernel land equivalents. + * + * Parameters: + * name - string value to parse. + * Object - pointer to an Object where the final result will be saved. + * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions. + * + * Returns: + * Length of the specified string value. + */ + +size_t +ParseRegistryObject(IN PCHAR name, OUT PCHAR *ObjectName, OUT BOOLEAN *wildcard) +{ + PCHAR key; + static CHAR buffer[MAX_PATH] = { 0 }; + + + if (_strnicmp(name, "HKEY_LOCAL_MACHINE\\", 19) == 0) + { + /* replace HKEY_LOCAL_MACHINE\ with kernel equivalent of \REGISTRY\MACHINE\ */ + + strcpy(name + 1, "\\REGISTRY\\MACHINE"); + name[18] = '\\'; + + key = name + 1; + } + else if (_strnicmp(name, "HKEY_USERS\\", 11) == 0) + { + /* replace HKEY_USERS\ with kernel equivalent of \REGISTRY\USER\ */ + + strcpy(buffer, "\\REGISTRY\\USER\\"); + strncat(buffer, name + 11, MAX_PATH - 12); + + key = buffer; + } + else + { + key = name; + } + + + *ObjectName = key; + + + return strlen(key); +} + + + +/* + * ParseFileObject() + * + * Description: + * Convert user land file object names into their kernel land equivalents. + * + * Parameters: + * name - string value to parse. + * Object - pointer to an Object where the final result will be saved. + * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions. + * + * Returns: + * Length of the specified string value. + */ + +size_t +ParseFileObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard) +{ + PCHAR filename; + static CHAR buffer[MAX_PATH] = { 0 }; //XXX not SMP safe + + + if (_strnicmp(name, "%systemdrive%:", 14) == 0) + { + name[12] = SystemDrive; + + name += 12; + } + + + // match drive wildcards such as "?:" and "*:" with "\Device\*" + if (name[1] == ':' && name[2] == '\\' && (name[0] == '?' || name[0] == '*')) + { +#if 0 + ConvertLongFileNameToShort(name, buffer + 7, MAX_PATH - 7); + + strcpy(buffer, "\\Device\\*"); + buffer[9] = '\\'; /* replace the zero terminator */ + + filename = buffer; +#endif + + strcpy(name - 7, "\\Device\\*"); + + /* + * replace "\Device\*" terminating zero with a '\' + * since name is just a pointer to FullName+7, FullName now contains + * \Device\*\ + */ + + name[2] = '\\'; + + filename = name - 7; + + + // mark the rule as wildcard even if the user (mistakenly) used "eq" + // XXX or should we throw an error if wildcard==0? + *wildcard = TRUE; + } + else if (isalpha(name[0]) && name[1] == ':') + { +#if 0 + CHAR buffer2[MAX_PATH]; + + + ConvertLongFileNameToShort(name, buffer2 + 4, MAX_PATH - 4); + + buffer2[0] = '\\'; + buffer2[1] = '?'; + buffer2[2] = '?'; + buffer2[3] = '\\'; + + if (ResolveFilename(buffer2, buffer, MAX_PATH) == FALSE) + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("ParseFileObject: ResolveFilename(%s) failed\n", name - 4)); +#endif + + + // match : drive specifications and prepend "\??\" to them + + *(name - 4) = '\\'; + *(name - 3) = '?'; + *(name - 2) = '?'; + *(name - 1) = '\\'; + + if (ResolveFilename(name - 4, buffer, MAX_PATH) == FALSE) + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("ParseFileObject: ResolveFilename(%s) failed\n", name - 4)); + + filename = buffer; + } + else if (_strnicmp(name, "%systemroot%\\", 13) == 0) + { + strcpy(buffer, SystemRoot); + strcat(buffer, name + 12); + + filename = buffer; + } + else if (_strnicmp(name, "\\pipe\\", 6) == 0) + { + strcpy(buffer, "\\device\\namedpipe"); + strcat(buffer, name + 5); + + filename = buffer; + } + else + { + filename = name; + } + + + *Object = filename; + + return strlen(filename); +} + + + +/* + * ParseProcessObject() + * + * Description: + * Convert user land process object names into their kernel land equivalents (strip the drive specification). + * + * Parameters: + * name - string value to parse. + * Object - pointer to an Object where the final result will be saved. + * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions. + * + * Returns: + * Length of the specified string value. + */ + +size_t +ParseProcessObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard) +{ + static CHAR buffer[MAX_PATH] = { 0 }; + + + if ((name = StripFileMacros(name, buffer, MAX_PATH)) == NULL) + return INVALID_OBJECT_SIZE; + + + *Object = name; + + return strlen(name); +} + + + +/* + * ParseBaseNamedObjectsObject() + * + * Description: + * Convert user land object names into their kernel land equivalents. + * + * Parameters: + * name - string value to parse. + * Object - pointer to an Object where the final result will be saved. + * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions. + * + * Returns: + * Length of the specified string value. + */ + +size_t +ParseBaseNamedObjectsObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard) +{ + PCHAR ObjectName; + + + /* + * if an object name does not start with a slash '\' then prepend '\BaseNamedObjects\' to it + */ + + if (name[0] != '\\') + { + //XXX this is a hack, we are prepending to our buffer, knowing that there is space there + strcpy(name - 18, "\\BaseNamedObjects"); + + *(name - 1) = '\\'; + + ObjectName = name - 18; + } + else + { + ObjectName = name; + } + + + *Object = ObjectName; + + return strlen(ObjectName); +} + + + +/* + * ParseMailslotObject() + * + * Description: + * Convert user land mailslot object names into their kernel land equivalents. + * + * Parameters: + * name - string value to parse. + * Object - pointer to an Object where the final result will be saved. + * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions. + * + * Returns: + * Length of the specified string value. + */ + +size_t +ParseMailslotObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard) +{ + PCHAR MailslotName; + + + /* + * if the mailslot name does not start with a slash '\' then prepend '\Device\Mailslot\' to the name + */ + + if (name[0] != '\\') + { + //XXX this is a hack, we are prepending to our buffer, knowing that there is space there + strcpy(name - 17, "\\Device\\Mailslot"); + + *(name - 1) = '\\'; + + MailslotName = name - 17; + } + else + { + MailslotName = name; + } + + + *Object = MailslotName; + + return strlen(MailslotName); +} + + + +/* + * ParseNamedpipeObject() + * + * Description: + * Convert user land namedpipe object names into their kernel land equivalents. + * + * Parameters: + * name - string value to parse. + * Object - pointer to an Object where the final result will be saved. + * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions. + * + * Returns: + * Length of the specified string value. + */ + +size_t +ParseNamedpipeObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard) +{ + PCHAR NamedpipeName; + + + /* + * if the namedpipe name does not start with a slash '\' then prepend '\Device\Namedpipe\' to the name + */ + + if (name[0] != '\\') + { + //XXX this is a hack, we are prepending to our buffer, knowing that there is space there + strcpy(name - 18, "\\Device\\Namedpipe"); + + *(name - 1) = '\\'; + + NamedpipeName = name - 18; + } + else + { + NamedpipeName = name; + } + + + *Object = NamedpipeName; + + return strlen(NamedpipeName); +} + + + +/* + * ParseDllObject() + * + * Description: + * Convert user land DLL object names into their kernel land equivalents. + * Since DLL rules are actually enforced by section rules, we just append DLL names to + * '\KnownDlls\' string which is used by section rules. + * + * Parameters: + * name - string value to parse. + * Object - pointer to an Object where the final result will be saved. + * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions. + * + * Returns: + * Length of the specified string value. + */ + +size_t +ParseDllObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard) +{ + PCHAR DllName; + + + /* + * if the DLL name does not start with a slash '\' then prepend '\KnownDlls\' to the name + */ + + if (name[0] != '\\') + { + strcpy(name - 11, "\\KnownDlls"); + + *(name - 1) = '\\'; + + DllName = name - 11; + } + else + { + DllName = name; + } + + + *Object = DllName; + + return strlen(DllName); +} + + + +/* + * ParseTimeObject() + * + * Description: + * Time rule specifications are not supposed to have any object names specified. + * Return an error. + * + * Parameters: + * name - string value to parse. + * Object - pointer to an Object where the final result will be saved. + * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions. + * + * Returns: + * An error. + */ + +size_t +ParseTimeObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard) +{ + return INVALID_OBJECT_SIZE; +} + + + +/* + * ParseServiceObject() + * + * Description: + * Convert user land service object names into their kernel land equivalents. + * Since service rules are actually enforced by registry rules, we just append service names + * to '\Registry\Machine\System\*ControlSet*\Services\' string which is used by registry rules. + * + * Parameters: + * name - string value to parse. + * Object - pointer to an Object where the final result will be saved. + * wildcard - pointer to a BOOLEAN that will indicate whether the specified value contained any regular expressions. + * + * Returns: + * Length of the specified string value. + * INVALID_OBJECT_SIZE is service name is too long. + */ + +size_t +ParseServiceObject(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard) +{ + static CHAR buffer[MAX_PATH] = { 0 }; //XXX not SMP safe + + + if (strlen(name) > 64) + { + return INVALID_OBJECT_SIZE; + } + + strcpy(buffer, "\\Registry\\Machine\\System\\*ControlSet*\\Services\\"); + strcat(buffer, name); + + *wildcard = TRUE; + + *Object = buffer; + + + return strlen(buffer); +} + + + +typedef size_t (*OBJECT_PARSER)(IN PCHAR name, OUT PCHAR *Object, OUT BOOLEAN *wildcard); +typedef UCHAR (*OPERATION_TYPE_PARSER)(IN PCHAR name); + + +/* in C++ these would be member methods */ + +struct _ObjectParseOps +{ + RULE_TYPE RuleType; + OBJECT_PARSER ObjectNameParser; + OPERATION_TYPE_PARSER OperationTypeParser; + +} ObjectParseOps[] = +{ + { RULE_FILE, ParseFileObject, ParseObjectOperation }, + { RULE_DIRECTORY, ParseFileObject, ParseDirectoryOperation }, + { RULE_MAILSLOT, ParseMailslotObject, ParseObjectOperation }, + { RULE_NAMEDPIPE, ParseNamedpipeObject, ParseObjectOperation }, + { RULE_REGISTRY, ParseRegistryObject, ParseObjectOperation }, + { RULE_SECTION, ParseBaseNamedObjectsObject, ParseObjectOperation }, + { RULE_DLL, ParseDllObject, ParseDllOperation }, + { RULE_EVENT, ParseBaseNamedObjectsObject, ParseCreateOpenOperation }, + { RULE_SEMAPHORE, ParseBaseNamedObjectsObject, ParseCreateOpenOperation }, + { RULE_JOB, ParseBaseNamedObjectsObject, ParseCreateOpenOperation }, + { RULE_MUTANT, ParseBaseNamedObjectsObject, ParseCreateOpenOperation }, + { RULE_PORT, ParseStub, ParsePortOperation }, + { RULE_SYMLINK, ParseStub, ParseCreateOpenOperation }, + { RULE_TIMER, ParseBaseNamedObjectsObject, ParseCreateOpenOperation }, + { RULE_PROCESS, ParseProcessObject, ParseProcessOperation }, + { RULE_DRIVER, ParseProcessObject, ParseDriverOperation }, + { RULE_DIROBJ, ParseStub, ParseCreateOpenOperation }, + { RULE_ATOM, ParseStub, ParseAtomOperation }, + { RULE_NETWORK, ParseNetworkObject, ParseNetworkOperation }, + { RULE_SERVICE, ParseServiceObject, ParseServiceOperation }, + { RULE_TIME, ParseTimeObject, ParseTimeOperation }, + { RULE_TOKEN, ParseTimeObject, ParseTokenOperation }, +}; + + + +/* + * PolicyParseActionClause() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * . + */ + +BOOLEAN +PolicyParseActionClause(PCHAR rule, ACTION_TYPE *ActionType, UCHAR *RuleNumber) +{ + UCHAR len = 0, num = 0; + + + SKIP_WHITESPACE(rule); + + + if (_strnicmp(rule, "permit", 6) == 0) + { + rule += 6; + + *ActionType = ACTION_PERMIT; + } + else if (_strnicmp(rule, "deny", 4) == 0) + { + rule += 4; + + *ActionType = ACTION_DENY; + } + else if (_strnicmp(rule, "quietdeny", 9) == 0) + { + rule += 9; + + *ActionType = ACTION_QUIETDENY; + } + else if (_strnicmp(rule, "log", 3) == 0) + { + rule += 3; + + *ActionType = ACTION_LOG; + } + else if (_strnicmp(rule, "ask", 3) == 0) + { + rule += 3; + + *ActionType = ACTION_ASK; + } + else + { + ABORT_PolicyParseRule(("Expecting to see 'permit', 'deny', 'quitedeny', 'log' or 'ask' clause. Got '%s'\n", rule)); + } + + + SKIP_WHITESPACE(rule); + + + /* EOL? */ + if (*rule == 0) + { + if (RuleNumber) + *RuleNumber = 0; + + return TRUE; + } + + + /* if it is not EOL then we expect to see "[rule DIGIT]" clause */ + if (_strnicmp(rule, "[rule", 5) == 0) + { + rule += 5; + } + else + { + ABORT_PolicyParseRule(("Expecting to see a rule clause. Got '%s'\n", rule)); + } + + + if (! IS_WHITESPACE(*rule)) + { + ABORT_PolicyParseRule(("Expecting to see white space. Got '%s'\n", rule)); + } + + SKIP_WHITESPACE(rule); + + + /* parse the rule number (a decimal digit) */ + while (*rule >= '0' && *rule <= '9') + { + /* don't overflow UCHAR values */ + if (++len > 2) + { + ABORT_PolicyParseRule(("The rule number is too long.\n")); + } + + num = num*10 + (*rule - '0'); + + ++rule; + } + + + SKIP_WHITESPACE(rule); + + + if (*rule != ']') + { + ABORT_PolicyParseRule(("Invalid rule clause: '%s'\n", rule)); + } + + + ++rule; + SKIP_WHITESPACE(rule); + + + /* expecting an EOL */ + if (*rule != 0) + { + ABORT_PolicyParseRule(("Expecting to see end of line. Got '%s'\n", rule)); + } + + + if (RuleNumber) + *RuleNumber = num; + + + return TRUE; +} + + + +/* + * PolicyParseOnOffClause() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * . + */ + +BOOLEAN +PolicyParseOnOffClause(PCHAR rule, BOOLEAN *OnOff) +{ + SKIP_WHITESPACE(rule); + + + if (_strnicmp(rule, "on", 2) == 0) + { + rule += 2; + + *OnOff = TRUE; + } + else if (_strnicmp(rule, "off", 3) == 0) + { + rule += 3; + + *OnOff = FALSE; + } + else + { + ABORT_PolicyParseRule(("Expecting to see 'on' or 'off' clause. Got '%s'\n", rule)); + } + + + SKIP_WHITESPACE(rule); + + /* expecting an EOL */ + if (*rule != 0) + { + ABORT_PolicyParseRule(("Expecting to see end of line. Got '%s'\n", rule)); + } + + return TRUE; +} + + + +/* + * VerifyToken2() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * . + */ + +UCHAR +VerifyToken2(PCHAR *rule, PCHAR token1, PCHAR token2) +{ + USHORT Token1Length, Token2Length; + + + ASSERT(rule && *rule); + ASSERT(token1); + ASSERT(token2); + + + SKIP_WHITESPACE(*rule); + + + Token1Length = (USHORT) strlen(token1); + Token2Length = (USHORT) strlen(token2); + + if (_strnicmp(*rule, token1, Token1Length) == 0) + { + *rule += Token1Length; + + if (! IS_WHITESPACE(**rule)) + { +// LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Expecting to see whitespace. Got '%s'\n", *rule)); + return FALSE; + } + + SKIP_WHITESPACE(*rule); + + return 1; + } + + + if (_strnicmp(*rule, token2, Token2Length) == 0) + { + *rule += Token2Length; + + if (! IS_WHITESPACE(**rule)) + { +// LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Expecting to see whitespace. Got '%s'\n", *rule)); + return FALSE; + } + + SKIP_WHITESPACE(*rule); + + return 2; + } + + + return 0; +} + + + +/* + * PolicyParsePolicyRule() + * + * Description: + * Parse a policy rule and adjust the policy default action. + * + * Parameters: + * pSecPolicy - security policy that the rule is going to be added to. + * Operation - ASCII operation (valid options are 'default' and 'path'). + * Rule - ASCII rule to parse. + * Critical - Boolean indicating whether the parser should abort parsing the policy due to a critical error. + * + * Returns: + * Nothing. + */ + +BOOLEAN +PolicyParsePolicyRule(OUT PSECURITY_POLICY pSecPolicy, IN PCHAR Operation, IN PCHAR rule, OUT BOOLEAN *Critical) +{ + ACTION_TYPE ActionType; + UCHAR RuleNumber; + + + *Critical = FALSE; + + + if (strlen(Operation) == 7 && _stricmp(Operation, "default") == 0) + { + if (PolicyParseActionClause(rule, &ActionType, &RuleNumber) == FALSE) + + ABORT_PolicyParseRule(("Invalid default policy action specification. Format: \"policy_default: (permit|deny|quietdeny|log|ask)\"\n")); + + + /* did we initialize default policy action already? */ + if (pSecPolicy->DefaultPolicyAction != ACTION_NONE) + + ABORT_PolicyParseRule(("Duplicate default policy action specification.\n")); + + + /* this still allows "policy_default: permit [rule 0]", oh well */ + if (RuleNumber != 0) + + ABORT_PolicyParseRule(("Rule clause cannot appear in default policy action specification.\n")); + + + pSecPolicy->DefaultPolicyAction = ActionType | ACTION_DEFAULT; + + return TRUE; + } + + + if (strlen(Operation) == 4 && _stricmp(Operation, "path") == 0) + { + CHAR szPath[MAX_PATH]; + CHAR szPolicyPath[MAX_PATH]; + + + _snprintf(szPath, MAX_PATH, "%S", gFilePath); + szPath[MAX_PATH - 1] = 0; + + + rule = StripFileMacros(rule, szPolicyPath, MAX_PATH); + + + KdPrint(("%s\n%s\n", szPath, rule)); + + if (WildcardMatch(szPath, rule) == WILDCARD_MATCH) + { + KdPrint(("paths match\n")); + return TRUE; + } + + + if (LearningMode == FALSE) + *Critical = TRUE; + + KdPrint(("paths do not match\n")); + + + return FALSE; + } + + + ABORT_PolicyParseRule(("Invalid policy operation '%s'. Valid options are 'default' and 'path'.\n", Operation)); +} + + + +/* + * PolicyParseProtectionRule() + * + * Description: + * Parse a protection rule and adjust the protection options such as buffer overflow protection + * and userland (dll injection) protection being on and off + * + * Parameters: + * pSecPolicy - security policy that the rule is going to be added to. + * Operation - ASCII operation. Valid options are 'overflow', 'userland', 'debugging', 'dos16', 'keyboard', 'modem', 'sniffer', 'extension' and 'all'. + * Rule - ASCII rule to parse. + * + * Returns: + * Nothing. + */ + +BOOLEAN +PolicyParseProtectionRule(PSECURITY_POLICY pSecPolicy, PCHAR Operation, PCHAR rule) +{ + BOOLEAN OnOff; + + + if (strlen(Operation) == 8 && _stricmp(Operation, "overflow") == 0) + { + if (PolicyParseOnOffClause(rule, &OnOff) == FALSE) + return FALSE; + + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning overflow protection %s\n", OnOff ? "on" : "off")); + + if (OnOff) + pSecPolicy->ProtectionFlags |= PROTECTION_OVERFLOW; + else + pSecPolicy->ProtectionFlags &= ~PROTECTION_OVERFLOW; + + return TRUE; + } + + + if (strlen(Operation) == 8 && _stricmp(Operation, "userland") == 0) + { + if (PolicyParseOnOffClause(rule, &OnOff) == FALSE) + return FALSE; + + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning userland protection %s\n", OnOff ? "on" : "off")); + + if (OnOff) + pSecPolicy->ProtectionFlags |= PROTECTION_USERLAND; + else + pSecPolicy->ProtectionFlags &= ~PROTECTION_USERLAND; + + return TRUE; + } + + + if (strlen(Operation) == 9 && _stricmp(Operation, "debugging") == 0) + { + if (PolicyParseOnOffClause(rule, &OnOff) == FALSE) + return FALSE; + + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning debugging protection %s\n", OnOff ? "on" : "off")); + + if (OnOff) + pSecPolicy->ProtectionFlags |= PROTECTION_DEBUGGING; + else + pSecPolicy->ProtectionFlags &= ~PROTECTION_DEBUGGING; + + return TRUE; + } + + + if (strlen(Operation) == 5 && _stricmp(Operation, "dos16") == 0) + { + if (PolicyParseOnOffClause(rule, &OnOff) == FALSE) + return FALSE; + + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning dos16/vdm protection %s\n", OnOff ? "on" : "off")); + + if (OnOff) + pSecPolicy->ProtectionFlags |= PROTECTION_VDM; + else + pSecPolicy->ProtectionFlags &= ~PROTECTION_VDM; + + return TRUE; + } + + + if (strlen(Operation) == 8 && _stricmp(Operation, "keyboard") == 0) + { + if (PolicyParseOnOffClause(rule, &OnOff) == FALSE) + return FALSE; + + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning keyboard logger protection %s\n", OnOff ? "on" : "off")); + + if (OnOff) + pSecPolicy->ProtectionFlags |= PROTECTION_KEYBOARD; + else + pSecPolicy->ProtectionFlags &= ~PROTECTION_KEYBOARD; + + return TRUE; + } + + + if (strlen(Operation) == 5 && _stricmp(Operation, "modem") == 0) + { + if (PolicyParseOnOffClause(rule, &OnOff) == FALSE) + return FALSE; + + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning modem protection %s\n", OnOff ? "on" : "off")); + + if (OnOff) + pSecPolicy->ProtectionFlags |= PROTECTION_MODEM; + else + pSecPolicy->ProtectionFlags &= ~PROTECTION_MODEM; + + return TRUE; + } + + + if (strlen(Operation) == 7 && _stricmp(Operation, "sniffer") == 0) + { + if (PolicyParseOnOffClause(rule, &OnOff) == FALSE) + return FALSE; + + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning sniffer protection %s\n", OnOff ? "on" : "off")); + + if (OnOff) + pSecPolicy->ProtectionFlags |= PROTECTION_SNIFFER; + else + pSecPolicy->ProtectionFlags &= ~PROTECTION_SNIFFER; + + return TRUE; + } + + + if (strlen(Operation) == 9 && _stricmp(Operation, "extension") == 0) + { + if (PolicyParseOnOffClause(rule, &OnOff) == FALSE) + return FALSE; + + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning extension protection %s\n", OnOff ? "on" : "off")); + + if (OnOff) + pSecPolicy->ProtectionFlags |= PROTECTION_EXTENSION; + else + pSecPolicy->ProtectionFlags &= ~PROTECTION_EXTENSION; + + return TRUE; + } + + + if (strlen(Operation) == 3 && _stricmp(Operation, "all") == 0) + { + if (PolicyParseOnOffClause(rule, &OnOff) == FALSE) + return FALSE; + + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseProtectionRule: Turning all protection %s\n", OnOff ? "on" : "off")); + + pSecPolicy->ProtectionFlags = OnOff == TRUE ? PROTECTION_ALL_ON : PROTECTION_ALL_OFF; + + return TRUE; + } + + + ABORT_PolicyParseRule(("Invalid protection operation '%s'. Valid options are 'overflow', 'userland', 'debugging', 'dos16', 'keyboard', 'modem', 'sniffer', 'extension' and 'all'.\n", Operation)); +} + + + +/* + * PolicyParseMediaRule() + * + * Description: + * Parse a media rule. + * + * Parameters: + * pSecPolicy - security policy that the rule is going to be added to. + * Operation - ASCII operation. The only valid option is 'access'. + * Rule - ASCII rule to parse. + * + * Returns: + * Nothing. + */ + +BOOLEAN +PolicyParseMediaRule(PSECURITY_POLICY pSecPolicy, PCHAR Operation, PCHAR rule) +{ + if (pSecPolicy != &gSecPolicy) + { + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("PolicyParseMediaRule: Media rules can be setup only in a global policy\n")); + return TRUE; + } + + + if (strlen(Operation) != 6 || _stricmp(Operation, "access") != 0) + ABORT_PolicyParseRule(("Invalid media operation '%s'. The only valid option is 'access'.\n", Operation)); + + + if (strlen(rule) == 6 && _stricmp(rule, "permit") == 0) + { + MediaRemovableFlags = MEDIA_REMOVABLE_PERMIT; + return TRUE; + } + + if (strlen(rule) == 4 && _stricmp(rule, "deny") == 0) + { + MediaRemovableFlags |= MEDIA_REMOVABLE_DISABLE; + return TRUE; + } + + if (strlen(rule) == 8 && _stricmp(rule, "readonly") == 0) + { + MediaRemovableFlags |= MEDIA_REMOVABLE_READONLY; + return TRUE; + } + + if (strlen(rule) == 9 && _stricmp(rule, "noexecute") == 0) + { + MediaRemovableFlags |= MEDIA_REMOVABLE_NOEXECUTE; + return TRUE; + } + + + ABORT_PolicyParseRule(("Expecting to see 'permit', 'deny', 'readonly', or 'noexecute' action. Got '%s'\n", rule)); +} + + + +/* + * InsertPolicyRule() + * + * Description: + * Adds a rule to a specified security policy (FIFO order). + * + * Parameters: + * pSecPolicy - security policy that the rule is going to be added to. + * PolicyRule - rule to add. + * RuleType - rule type (file, network, etc). + * + * Returns: + * Nothing. + */ + +VOID +InsertPolicyRule(PSECURITY_POLICY pSecPolicy, PPOLICY_RULE PolicyRule, RULE_TYPE RuleType) +{ + KIRQL irql; + PPOLICY_RULE tmp; + + + ASSERT(RuleType < RULE_LASTONE); + + + KeAcquireSpinLock(&pSecPolicy->SpinLock, &irql); + + + if (pSecPolicy->RuleList[RuleType] == NULL) + { + pSecPolicy->RuleList[RuleType] = PolicyRule; + + KeReleaseSpinLock(&pSecPolicy->SpinLock, irql); + + return; + } + + /* find the last rule and link the new rule to it */ + tmp = pSecPolicy->RuleList[RuleType]; + + while (tmp->Next) + { + tmp = tmp->Next; + } + + tmp->Next = PolicyRule; + + + KeReleaseSpinLock(&pSecPolicy->SpinLock, irql); +} + + + +/* + * PolicyParseObjectRule() + * + * Description: + * Parse an object rule of the following format: + * (name|address) (eq|match) "" then (deny|quitedeny|permit|log) + * Rule can also consist of just (deny|quitedeny|permit|log) + * + * example1: name match "c:\file*" then deny + * example2: address eq "192.168.0.1" then log + * example3: quietdeny + * + * Parameters: + * pSecPolicy - security policy that the rule is going to be added to. + * RuleType - rule type (file, network, etc). + * Operation - ASCII operation (read, write, etc). + * Rule - ASCII rule to parse. NOTE: this field gets clobbered. + * + * Returns: + * Nothing. + */ + +BOOLEAN +PolicyParseObjectRule(PSECURITY_POLICY pSecPolicy, RULE_TYPE RuleType, PCHAR Operation, PCHAR rule) +{ + PCHAR name = NULL; + PCHAR OriginalRule = rule; + int i, TotalStars; + BOOLEAN wildcard, WildcardWarning = FALSE; + BOOLEAN ParseLastToken = FALSE; + ACTION_TYPE ActionType; + MATCH_TYPE MatchType; + PPOLICY_RULE PolicyRule; + UCHAR OperationType; + PCHAR ObjectName = NULL; + size_t ObjectSize = 0; + UCHAR RuleNumber; + + + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseObjectRule: Parsing rule '%s': '%s'\n", Operation, rule)); + + + /* + * First token can be "name" or "address" for network rules + * Alternatively, the entire rule can consist of an action clause (such as permit, deny, quietdeny or log) + */ + + switch (VerifyToken2(&rule, "name", "address")) + { + /* matched token1 - "name" */ + case 1: break; + + /* matched token2 - "address" */ + case 2: + { + /* only network rules can substitute "address" for "name" */ + + if (RuleType != RULE_NETWORK) + + ABORT_PolicyParseRule(("Expecting to see 'name'. Got 'address'\n")); + + break; + } + + /* didn't match "name" or "address". try to parse as an action clause */ + default: + { + ParseLastToken = TRUE; + goto ParseLastToken; + } + } + + + /* + * Second token should be "eq" or "match" + */ + + switch (VerifyToken2(&rule, "eq", "match")) + { + /* matched token1 - "eq" */ + case 1: wildcard = FALSE; break; + + /* matched token2 - "match" */ + case 2: wildcard = TRUE; break; + + /* didn't match "eq" or "match" */ + default: ABORT_PolicyParseRule(("Expecting to see 'eq' or 'match'. Got '%s'\n", rule)); + } + + + /* + * Third token is the object names in quotes + */ + + /* parse the object name surrounded by quotes: "" */ + + if (*rule++ != '"') + ABORT_PolicyParseRule(("Initial quote character not found. Object names should be surrounded by quotes.\n")); + + + name = rule; + + TotalStars = i = 0; + + + while (*rule != 0 && *rule != '"') + { + if (i >= POLICY_MAX_OBJECT_NAME_LENGTH-1) + ABORT_PolicyParseRule(("Object name '%s' is too long.\nMaximum name length is %d characters.\n", rule - POLICY_MAX_OBJECT_NAME_LENGTH, POLICY_MAX_OBJECT_NAME_LENGTH)); + + // fail bad regexes + if (*rule == '*') + { + if (++TotalStars > POLICY_TOTAL_NUMBER_OF_STARS) + ABORT_PolicyParseRule(("Invalid regular expression. Maximum of %d stars are allowed\n", POLICY_TOTAL_NUMBER_OF_STARS)); + } + + + if (wildcard == FALSE && (*rule == '*' || *rule == '?') && WildcardWarning == FALSE) + { + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("%S:%d:\n", gPolicyFilename, gPolicyLineNumber)); + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Found a regular expression with an 'eq' operator. Use 'match' operator to enable regular expressions.\n")); + WildcardWarning = TRUE; + } + + + ++i; + ++rule; + } + + + if (i == 0 || *rule++ != '"') + ABORT_PolicyParseRule(("Final quote character not found. Object names should be surrounded by quotes.\n")); + + name[i] = 0; + + + if (! IS_WHITESPACE(*rule)) + { + ABORT_PolicyParseRule(("Expecting to see white space. Got '%s'\n", rule)); + } + + SKIP_WHITESPACE(rule); + + + /* + * Fourth token is "then" + */ + + if (VerifyToken2(&rule, "then", "") != 1) + ABORT_PolicyParseRule(("Expecting to see 'then'. Got '%s'\n", rule)); + + /* + * Fifth/Last token is "permit", "deny", "quietdeny", "log" or "ask" + */ + +ParseLastToken: + + if (PolicyParseActionClause(rule, &ActionType, &RuleNumber) == FALSE) + + return FALSE; + + + /* + * Rule parsed ok. Create a new rule. + */ + + if (RuleType > sizeof(ObjectParseOps) / sizeof(ObjectParseOps[0])) + ABORT_PolicyParseRule(("Invalid rule type\n")); + + + if (name) + { + if ((ObjectSize = ObjectParseOps[RuleType].ObjectNameParser(name, &ObjectName, &wildcard)) == INVALID_OBJECT_SIZE) + ABORT_PolicyParseRule(("Invalid object name '%s'\n", name)); + + MatchType = (wildcard == TRUE ? MATCH_WILDCARD : MATCH_SINGLE); + } + else + { + ObjectSize = 0; + MatchType = MATCH_ALL; + } + + + if ((OperationType = ObjectParseOps[RuleType].OperationTypeParser(Operation)) == OP_INVALID) + ABORT_PolicyParseRule(("Invalid operation '%s'\n", Operation)); + + + /* POLICY_RULE already includes 1 character for the name buffer */ + + PolicyRule = ExAllocatePoolWithTag(NonPagedPool, sizeof(POLICY_RULE) + ObjectSize, _POOL_TAG); + if (PolicyRule == NULL) + { + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Object policy parser is out of memory\n")); + return FALSE; + } + + RtlZeroMemory(PolicyRule, sizeof(POLICY_RULE)); + + PolicyRule->ActionType = ActionType; + PolicyRule->MatchType = MatchType; + PolicyRule->OperationType = OperationType; + + PolicyRule->RuleNumber = RuleNumber; + PolicyRule->PolicyLineNumber = gPolicyLineNumber; + PolicyRule->pSecurityPolicy = pSecPolicy; + + + /* ObjectSize can be 0 for MATCH_ALL rules */ + if (ObjectSize) + { + PolicyRule->NameLength = (USHORT) ObjectSize; + strcpy(PolicyRule->Name, (PCHAR) ObjectName); + } + + + /* + * Some rules (such as DLL) are actually enforced by different rules (i.e. section). + * If LearningMode = TRUE then there is no need to convert + */ + + if (LearningMode == FALSE) + { + /* DLL rules are enforced by section rules. */ + if (RuleType == RULE_DLL) + RuleType = RULE_SECTION; + + /* Service rules are enforced by registry rules. */ + if (RuleType == RULE_SERVICE) + RuleType = RULE_REGISTRY; + } + + + InsertPolicyRule(pSecPolicy, PolicyRule, RuleType); + + + return TRUE; +} + + + +/* + * PolicyParseSyscallRule() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * Nothing. + */ + +BOOLEAN +PolicyParseSyscallRule(PSECURITY_POLICY pSecPolicy, PCHAR SyscallName, PCHAR rule) +{ + PPOLICY_RULE PolicyRule; + KIRQL irql; + ACTION_TYPE ActionType; + ULONG SyscallNameIndex; + BOOLEAN AcceptAll = FALSE; + + +#if HOOK_SYSCALLS + + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_VERBOSE, ("PolicyParseSyscallRule: Parsing syscall '%s' rule: '%s'\n", SyscallName, rule)); + + + /* expecting to see "permit", "deny", "quietdeny" or "log" */ + if (PolicyParseActionClause(rule, &ActionType, NULL) == FALSE) + { +// LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_DEBUG, ("PolicyParseSyscallRule: PolicyParseActionClause failed\n")); + return FALSE; + } + +#if 0 + /* + * all the special system calls such as OpenFile and CreateProcess have already been hooked, it should + * be safe to hook everything else. If a special system call is specified then HookSystemServiceByName + * will just silently fail since it's already hooked. + */ + + if (HookSystemServiceByName(SyscallName, NULL/*USE_DEFAULT_HOOK_FUNCTION*/) == FALSE) + { + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Unknown syscall '%s'\n", SyscallName)); + return FALSE; + } +#endif + + + if (strlen(SyscallName) == 3 && _stricmp(SyscallName, "all") == 0) + { + AcceptAll = TRUE; + } + else + { + SyscallNameIndex = FindSystemServiceNumber(SyscallName); + if (SyscallNameIndex == -1) + { + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_DEBUG, ("PolicyParseSyscallRule: Syscall name '%s' not found\n", SyscallName)); + return FALSE; + } + } + + + /* allocate enough memory to hold a bit array for all existing system calls */ + + if (pSecPolicy->RuleList[ RULE_SYSCALL ] == NULL) + { + /* + * Take into account 1 ULONG that is already preallocated in POLICY_RULE. + */ + + USHORT Size = 1 + (USHORT) (ZwCallsNumber - NumberOfBitsInUlong) / 8; + + + PolicyRule = ExAllocatePoolWithTag(NonPagedPool, sizeof(POLICY_RULE) + Size, _POOL_TAG); + if (rule == NULL) + { + LOG(LOG_SS_POLICY_PARSER, LOG_PRIORITY_WARNING, ("Policy parser is out of memory\n")); + return FALSE; + } + + RtlZeroMemory(PolicyRule, sizeof(POLICY_RULE) + Size); + + + /* if default action is permit, then fill the entire bit array with 1's */ + if (pSecPolicy->DefaultPolicyAction == ACTION_PERMIT) + { + RtlFillMemory(&PolicyRule->ServiceBitArray, sizeof(PolicyRule->ServiceBitArray) + Size, 0xFF); + } + + + KeAcquireSpinLock(&pSecPolicy->SpinLock, &irql); + + pSecPolicy->RuleList[ RULE_SYSCALL ] = PolicyRule; + + KeReleaseSpinLock(&pSecPolicy->SpinLock, irql); + } + else + { + PolicyRule = pSecPolicy->RuleList[ RULE_SYSCALL ]; + } + + + // syscall_all: permit + // syscall_all: deny + + // syscall_blah: permit + // syscall_all: deny + + if (AcceptAll == TRUE) + ;//XXX + + { + /* Calculate the bit array ULONG we need to modify (bit array consists of a bunch of ULONGs) */ + ULONG UlongCount = SyscallNameIndex >> UlongBitShift; + + /* Choose the correct bit array ULONG */ + PULONG BitArray = &PolicyRule->ServiceBitArray[0] + UlongCount; + + + //XXX what about log? + if (ActionType == ACTION_PERMIT) + { + /* set the bit */ + *BitArray |= 1 << (SyscallNameIndex - (UlongCount << UlongBitShift)); + } + else if (ActionType >= ACTION_DENY) + { + /* reset the bit */ + *BitArray &= ~( 1 << (SyscallNameIndex - (UlongCount << UlongBitShift)) ); + } + } + +#endif + + + return TRUE; +} + + + +/* + * WildcardMatch() + * + * Description: + * Simple regex algorithm. Supports '?' for a single character match (does not match EOF) and + * '*' for multiple characters (must reside in the same directory). + * + * i.e. c:\temp\*\blah will match c:\temp\temp2\blah but not c:\temp\temp2\temp3\blah + * + * NOTE: WildcardMatch() is at least 10x slower than simple _stricmp(). + * + * Parameters: + * path - ASCII pathname. + * regex - regular expression to match + * + * Returns: + * WILDCARD_MATCH (0) in case of a match, WILDCARD_NO_MATCH (1) otherwise. + */ + +int +WildcardMatch(PCHAR path, PCHAR regex) +{ + BOOLEAN MultipleDirectoryMatch = FALSE, SkippedOverWhitespace = FALSE, ShortFileName = FALSE; + + + if (path == NULL || regex == NULL) + return WILDCARD_NO_MATCH; + + + while (*regex) + { + /* + * SPECIAL CASE. + * Try to deal with short names (longna~1.txt vs long name.txt). + * when we encounter a ~X where X is a digit we skip over it and rewind regex + * to either the next '\' or whatever the next path character is. + * + * The reason for this is when we encounter a long directory it will be abbreviated as follows + * c:\Documents and Settings\... -> c:\DOCUME~1\... + * Thus by matching "DOCUME" and then skipping over ~1 in the pathname and by rewinding regex + * until the next '\' we match the two directories. + * + * The long filenames are matched and abbreviated as follows + * c:\longfilename.txt -> c:\longfi~1.txt + * Thus we match "longfi", skip over ~1 in the pathname and rewind regex until we match + * the next path character which is '.', then we match ".txt" + * + * The following scheme was mostly designed to deal with "c:\documents and settings" and + * "c:\program files" directories since they are two very common long names. It breaks + * in a lot of different cases. Consider the following abbreviation list + * + * LONGNA~1 long name1 + * LONGNA~2 long name2 + * LONGNA~3 long name3 + * LONGNA~4 long name4 + * LOA926~1 long name5 + * LOB926~1 long name6 + * + * When more than four names match to the same short name prefix, Windows switches to an + * alternative hash based schemes which is not handled by our code. + * + * Another restriction (there are probably more) has to do with long extensions which are abbreviated + * as follows: file.html.text -> filete~1.htm. The following code does not handle this case either. + * + * All in all, the following code is an ugly hack that is designed to work for most common cases + * with a least amount of effort. + */ + + if (*path == '~' && isdigit(*(path + 1))) + { + /* rewind regex until we see '\' or whichever character (ie '.' as in ~1.txt) follows ~X in the path */ + while (*regex && *regex != '\\' && *regex != *(path + 2) /*&& *regex != '*' && *regex != '?'*/) + ++regex; + + /* skip over ~X */ + path += 2; + + ShortFileName = TRUE; + } + + + if (*regex == '*') + { + PCHAR str; + + /* + * match one or more characters + */ + + /* if regular expression ends with '*', automatically match the rest of the pathname */ + if (*(regex + 1) == 0) + return WILDCARD_MATCH; + + if (*(regex + 1) == '*') + { + ++regex; + MultipleDirectoryMatch = TRUE; + } + + str = path; + while (*str) + { + if (WildcardMatch(str, regex+1) == WILDCARD_MATCH) + return WILDCARD_MATCH; + + if (MultipleDirectoryMatch == FALSE && *str == '\\') + break; + + ++str; + } + } + else if (*regex == '?') + { + /* + * match one character + */ + + if (*path == 0 || *path == '\\') + return WILDCARD_NO_MATCH; + + ++path; + } + else if (*regex == '\\') + { + /* if we skipped over whitespace but did not match a short name then bail out as not skipping over + whitespace would not get us this far anyway */ + if (SkippedOverWhitespace == TRUE && ShortFileName == FALSE) + return WILDCARD_NO_MATCH; + + ShortFileName = FALSE; + SkippedOverWhitespace = FALSE; + + /* + * match one or more slashes + */ + + if (*path++ != '\\') + return WILDCARD_NO_MATCH; + + while (*path == '\\') + ++path; + } + else + { + /* + * match all other characters + */ + +// if (toupper(*path++) != toupper(*regex)) +// return WILDCARD_NO_MATCH; + + if (toupper(*path) != toupper(*regex)) + { + /* + * Skip over whitespace to match long filenames which are abbreviated with all + * whitespace stripped. In order not to match two filenames with different amount + * of whitespace, we insert an additional check when we reach '\' once we found out + * whether we are working with an abbreviated name. + */ + if (*regex == ' ') + SkippedOverWhitespace = TRUE; + else + return WILDCARD_NO_MATCH; + } + else + { + ++path; + } + } + + ++regex; + } + + + /* make sure pathname is not longer than the regex */ + return *path == 0 ? WILDCARD_MATCH : WILDCARD_NO_MATCH; +} + + + +/* + * PolicyCheckPolicy() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * . + */ + +ACTION_TYPE +PolicyCheckPolicy(PSECURITY_POLICY pSecPolicy, RULE_TYPE RuleType, PCHAR Object, UCHAR OperationType, ACTION_TYPE DefaultAction, UCHAR *RuleNumber, PWSTR *PolicyFilename, USHORT *PolicyLineNumber) +{ + PPOLICY_RULE r; + ACTION_TYPE ret = DefaultAction; + KIRQL irql; + size_t len; + + + ASSERT(pSecPolicy); + ASSERT(PolicyFilename && PolicyLineNumber); + ASSERT(RuleNumber); + + + if (Object) + len = strlen(Object); + + *PolicyFilename = NULL; + *PolicyLineNumber = 0; + *RuleNumber = 0; + + + if (pSecPolicy->Initialized == FALSE) + + return ACTION_NONE; + + + KeAcquireSpinLock(&pSecPolicy->SpinLock, &irql); + + + *PolicyFilename = pSecPolicy->Name; + + + r = pSecPolicy->RuleList[RuleType]; + + while (r) + { +//XXX at least for registry keys there is no point in comparing the first 10 characters since they will always be \Registry\ ? + + if ( (r->MatchType == MATCH_ALL) || + (r->MatchType == MATCH_SINGLE && len == r->NameLength && _stricmp(Object, r->Name) == 0) || + (r->MatchType == MATCH_WILDCARD && WildcardMatch(Object, r->Name) == WILDCARD_MATCH) ) + { + if (Object) + LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("%d PolicyCheckPolicy: matched '%s' (%s) (%x %x %x)\n", CURRENT_PROCESS_PID, Object, r->Name, r->MatchType, r->OperationType, OperationType)); + else + LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("%d PolicyCheckPolicy: matched RuleType %d MatchType %d OpType %d\n", CURRENT_PROCESS_PID, RuleType, r->MatchType, r->OperationType)); + + +// if (r->OperationType == OP_APPEND) +// { +// ret = ACTION_PROCESS; +// break; +// } + + + /* + * (r->OperationType & OperationType) == r->OperationType + * (r->OperationType & OperationType) + * + * policy_default: deny + * file_read: file.txt + * + * del file.txt (opens file for read + write) + * + * (read & read_write) = read + * + * successfully overwrites the file! + */ + + /* + * (r->OperationType & OperationType) == OperationType + * + * policy_default: permit + * file_write: file.txt deny + * file_read: file.txt + * + * del file.txt (opens file for read + write) + * + * (write & read_write) != read_write + * + * successfully deletes the file! + */ + + // XXX if we only allow reading but both read+execute are requested, the following + // if will match! (bad in deny all scenario!) + if (r->OperationType == OP_ALL || r->OperationType & OperationType) +// if (r->OperationType == OP_ALL || (r->OperationType & OperationType) == r->OperationType) +// if (r->OperationType == OP_ALL || (r->OperationType & OperationType) == OperationType) + { + ret = r->ActionType; + + + /* remember which rule caused this alert */ + *PolicyLineNumber = r->PolicyLineNumber; + *RuleNumber = r->RuleNumber; + + + LOG(LOG_SS_POLICY, LOG_PRIORITY_VERBOSE, ("%d PolicyCheckPolicy: %s access to %d\n", + CURRENT_PROCESS_PID, ret >= ACTION_DENY ? "deny" : "permit", + (ULONG) PsGetCurrentProcessId())); + + break; + } + } + + r = (PPOLICY_RULE) r->Next; + } + + KeReleaseSpinLock(&pSecPolicy->SpinLock, irql); + + + return ret; +} + + + +/* + * PolicyCheck() + * + * Description: + * Verifies whether a specified action (rule (RuleType) + object (arg) ) are allowed + * by a global and current process security policies. + * + * Parameters: + * RuleType - rule type (file rule, registry, etc). + * Object - Object name. + * OperationType - type of operation carried out (read, write, etc). + * RuleNumber - number of the rule that triggered the alert (if it did) + * PolicyFilename - name of the policy where the rule, that triggered the alert, was specified + * PolicyLineNumber - policy line where the rule, that triggered the alert, was specified + * + * Returns: + * ACCESS_NONE, ACCESS_PERMIT, ACCESS_DENY or ACCESS_QUIETDENY depending on a security policy. + */ + +ACTION_TYPE +PolicyCheck(RULE_TYPE RuleType, PCHAR Object, UCHAR OperationType, UCHAR *RuleNumber, PWSTR *PolicyFilename, USHORT *PolicyLineNumber) +{ + PIMAGE_PID_ENTRY p, prev; + PWSTR GlobalPolicyFilename, ProcessPolicyFilename; + USHORT GlobalPolicyLineNumber, ProcessPolicyLineNumber; + UCHAR GlobalRuleNumber, ProcessRuleNumber; + ACTION_TYPE GlobalAction, ProcessAction = ACTION_PERMIT_DEFAULT, ReturnAction; + ULONG ProcessId = CURRENT_PROCESS_PID; + + + /* don't mess with kernel initiated calls */ + + if (KeGetPreviousMode() != UserMode || KeGetCurrentIrql() != PASSIVE_LEVEL) + + return ACTION_PERMIT; + + + /* + * As most of system calls PolicyCheck() this is a convinient place to verify + * user return address since it needs to be done for all calls + */ + + VerifyUserReturnAddress(); + + + /* + * First verify against the global security policy + */ + + GlobalAction = PolicyCheckPolicy(&gSecPolicy, RuleType, Object, OperationType, gSecPolicy.DefaultPolicyAction, &GlobalRuleNumber, &GlobalPolicyFilename, &GlobalPolicyLineNumber); + + /* + * Then against process specific policy + */ + + /* find the process specific policy */ + + p = FindImagePidEntry(ProcessId, 0); + + if (p) + { + ProcessAction = PolicyCheckPolicy(&p->SecPolicy, RuleType, Object, OperationType, p->SecPolicy.DefaultPolicyAction, &ProcessRuleNumber, &ProcessPolicyFilename, &ProcessPolicyLineNumber); + } + + +// KdPrint(("object %s %d %d action %x %x", Object, RuleType, OperationType, GlobalAction, ProcessAction)); + + /* + * return the most stringent possible action + */ + + /* Exception #1: explicit process permit/log overrides general global deny */ + if ((ProcessAction == ACTION_PERMIT || ProcessAction == ACTION_LOG) && + (GlobalAction & (ACTION_ASK | ACTION_DENY))) + { + *PolicyFilename = ProcessPolicyFilename; + *PolicyLineNumber = ProcessPolicyLineNumber; + *RuleNumber = ProcessRuleNumber; + return ProcessAction; + } + + /* Exception #2: explicit global permit overrides process default deny */ + if (GlobalAction == ACTION_PERMIT && (ProcessAction & ACTION_DEFAULT)) + { + *PolicyFilename = GlobalPolicyFilename; + *PolicyLineNumber = GlobalPolicyLineNumber; + *RuleNumber = GlobalRuleNumber; + return GlobalAction; + } + + if (GlobalAction & ACTION_DENY) + { *PolicyFilename = GlobalPolicyFilename; *PolicyLineNumber = GlobalPolicyLineNumber; *RuleNumber = GlobalRuleNumber; ReturnAction = GlobalAction; goto done; } + + if (ProcessAction & ACTION_DENY) + { *PolicyFilename = ProcessPolicyFilename; *PolicyLineNumber = ProcessPolicyLineNumber; *RuleNumber = ProcessRuleNumber; ReturnAction = ProcessAction; goto done; } + + + if (GlobalAction & ACTION_LOG) + { *PolicyFilename = GlobalPolicyFilename; *PolicyLineNumber = GlobalPolicyLineNumber; *RuleNumber = GlobalRuleNumber; ReturnAction = GlobalAction; goto done; } + + if (ProcessAction & ACTION_LOG) + { *PolicyFilename = ProcessPolicyFilename; *PolicyLineNumber = ProcessPolicyLineNumber; *RuleNumber = ProcessRuleNumber; ReturnAction = ProcessAction; goto done; } + + + if (GlobalAction & ACTION_ASK) + { *PolicyFilename = GlobalPolicyFilename; *PolicyLineNumber = GlobalPolicyLineNumber; *RuleNumber = GlobalRuleNumber; ReturnAction = GlobalAction; goto done; } + + if (ProcessAction & ACTION_ASK) + { *PolicyFilename = ProcessPolicyFilename; *PolicyLineNumber = ProcessPolicyLineNumber; *RuleNumber = ProcessRuleNumber; ReturnAction = ProcessAction; goto done; } + + + if (GlobalAction & ACTION_PERMIT) + { *PolicyFilename = GlobalPolicyFilename; *PolicyLineNumber = GlobalPolicyLineNumber; *RuleNumber = GlobalRuleNumber; ReturnAction = GlobalAction; goto done; } + + if (ProcessAction & ACTION_PERMIT) + { *PolicyFilename = ProcessPolicyFilename; *PolicyLineNumber = ProcessPolicyLineNumber; *RuleNumber = ProcessRuleNumber; ReturnAction = ProcessAction; goto done; } + + + LOG(LOG_SS_POLICY, LOG_PRIORITY_DEBUG, ("object %s %d %d action %x %x\n", Object, RuleType, OperationType, GlobalAction, ProcessAction)); + + *PolicyFilename = NULL; + *PolicyLineNumber = 0; + + + return ProcessAction; + + +done: + + /* + * if we are booting up then don't deny any system calls to prevent a machine + * from not booting up + */ + + if (BootingUp == TRUE && (ReturnAction == ACTION_ASK || (ReturnAction & ACTION_DENY))) + ReturnAction = ACTION_LOG; + + return ReturnAction; +} diff --git a/policy.h b/policy.h new file mode 100644 index 0000000..a85c4fc --- /dev/null +++ b/policy.h @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * policy.h + * + * Abstract: + * + * This module defines various types used by security policy related routines. + * + * Author: + * + * Eugene Tsyrklevich 16-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __POLICY_H__ +#define __POLICY_H__ + + +#define POLICY_MAX_SERVICE_NAME_LENGTH 64 +#define POLICY_MAX_OBJECT_NAME_LENGTH 192 +#define POLICY_MAX_RULE_LENGTH 256 + +// maximum number of '*' characters in a regex +#define POLICY_TOTAL_NUMBER_OF_STARS 5 + +#define isalpha(c) ( ((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') ) + +#if 0 +typedef enum _ActionType +{ + ACTION_NONE=0, + ACTION_PERMIT, + ACTION_PERMIT_DEFAULT, + ACTION_LOG, + ACTION_LOG_DEFAULT, + ACTION_PROCESS, /* further processing is required */ + ACTION_RESERVED1, + ACTION_RESERVED2, + ACTION_RESERVED3, + ACTION_TERMINATE, /* terminate process */ + ACTION_ASK, /* XXX user prompt (interactive session only?) */ + ACTION_ASK_PERMIT, /* User chose permit */ + ACTION_ASK_LOG, /* User chose log */ + ACTION_ASK_TERMINATE, /* User chose terminate */ + ACTION_DENY, /* all actions listed after ACTION_DENY are treated as DENY actions */ + ACTION_ASK_DENY, /* User chose deny */ + ACTION_DENY_DEFAULT, /* default deny policy action, used to distinguish between default and explicit deny actions */ + ACTION_QUIETDENY, /* deny but do not log */ + ACTION_QUIETDENY_DEFAULT, /* deny but do not log (default action) */ + +} ACTION_TYPE; +#endif + + +typedef unsigned char ACTION_TYPE; + +#define ACTION_DEFAULT (1 << 7) +#define ACTION_DENY (1 << 6) +#define ACTION_PERMIT (1 << 5) +#define ACTION_LOG (1 << 4) +#define ACTION_TERMINATE (1 << 3) + +#define ACTION_NONE 0 +#define ACTION_ASK 1 +#define ACTION_ASK_PERMIT (ACTION_ASK | ACTION_PERMIT) +#define ACTION_ASK_LOG (ACTION_ASK | ACTION_LOG) +#define ACTION_ASK_TERMINATE (ACTION_ASK | ACTION_TERMINATE) +#define ACTION_ASK_DENY (ACTION_ASK | ACTION_DENY) +#define ACTION_QUIETDENY (2 | ACTION_DENY) +#define ACTION_PROCESS 3 +#define ACTION_RESERVED1 4 +#define ACTION_RESERVED2 5 +#define ACTION_RESERVED3 6 +#define ACTION_RESERVED4 7 + +#define ACTION_DENY_DEFAULT (ACTION_DENY | ACTION_DEFAULT) +#define ACTION_PERMIT_DEFAULT (ACTION_PERMIT | ACTION_DEFAULT) +#define ACTION_LOG_DEFAULT (ACTION_LOG | ACTION_DEFAULT) +#define ACTION_QUIETDENY_DEFAULT (ACTION_QUIETDENY | ACTION_DEFAULT) +#define ACTION_ASK_DEFAULT (ACTION_ASK | ACTION_DEFAULT) + + +#define DEFAULT_POLICY_ACTION ACTION_PERMIT_DEFAULT + + +/* + * WARNING: ObjectParseOps (policy.c) && RuleTypeData (learn.c) structures depend on the order of the + * following enum values + * + * RuleType enumerates all possible object types. + * (in C++ we would have a separate class for each) + */ + +typedef enum _RuleType +{ + RULE_FILE = 0, + RULE_DIRECTORY, + RULE_MAILSLOT, + RULE_NAMEDPIPE, + RULE_REGISTRY, + RULE_SECTION, + RULE_DLL, + RULE_EVENT, + RULE_SEMAPHORE, + RULE_JOB, + RULE_MUTANT, + RULE_PORT, + RULE_SYMLINK, + RULE_TIMER, + RULE_PROCESS, + RULE_DRIVER, + RULE_DIROBJ, + RULE_ATOM, + + RULE_NETWORK, + RULE_SERVICE, + RULE_TIME, + RULE_TOKEN, + RULE_SYSCALL, + RULE_LASTONE, /* not a real rule, just a convinient way of iterating through all rules (i < RULE_LASTONE) */ + +} RULE_TYPE; + + +typedef enum _MatchType +{ + MATCH_SINGLE = 0, + MATCH_WILDCARD, + MATCH_ALL, + MATCH_NONE + +} MATCH_TYPE; + + +typedef enum _AlertPriority +{ + ALERT_PRIORITY_HIGH = 1, + ALERT_PRIORITY_MEDIUM, + ALERT_PRIORITY_LOW, + ALERT_PRIORITY_INFO, + +} ALERT_PRIORITY; + + +/* + * Operation Types + */ + + +#define OP_INVALID 0x00 +#define OP_NONE 0x00 + +// file ops + +#define OP_READ 0x01 +#define OP_WRITE 0x02 +#define OP_READ_WRITE (OP_READ | OP_WRITE) +#define OP_EXECUTE 0x04 +#define OP_DELETE 0x08 +#define OP_APPEND 0x10 + +// dirobj & job ops +#define OP_CREATE 0x01 +#define OP_OPEN 0x02 + +// directory ops + +#define OP_DIR_TRAVERSE 0x01 +#define OP_DIR_CREATE 0x02 + +// process ops + +#define OP_PROC_EXECUTE 0x01 +#define OP_PROC_OPEN 0x02 + +// port ops + +#define OP_PORT_CONNECT 0x01 +#define OP_PORT_CREATE 0x02 + +// network ops + +#define OP_TCPCONNECT 0x01 +#define OP_UDPCONNECT 0x02 +#define OP_CONNECT 0x03 +#define OP_BIND 0x04 + +// atom ops + +#define OP_FIND 0x01 +#define OP_ADD 0x02 + +// service ops + +#define OP_SERVICE_START 0x01 +#define OP_SERVICE_STOP 0x02 +#define OP_SERVICE_CREATE 0x03 +#define OP_SERVICE_DELETE 0x04 + +// dll/driver ops + +#define OP_LOAD 0x01 +#define OP_REGLOAD 0x02 +#define OP_UNLOAD 0x03 // XXX 0x04? + +// time change op + +#define OP_TIME_CHANGE 0x01 + +// vdm ops + +#define OP_VDM_USE 0x01 + +// debug ops + +#define OP_DEBUG 0x01 + +// token ops + +#define OP_TOKEN_MODIFY 0x01 + +// buffer overflow protection "virtual op" + +#define OP_INVALIDCALL 0x01 + + +#define OP_ALL 0xFF + + +// forward declaration +typedef struct _SECURITY_POLICY SECURITY_POLICY, *PSECURITY_POLICY; + + +/* Rule should really be a class */ + +typedef struct _POLICY_RULE +{ + struct _POLICY_RULE *Next; + + PSECURITY_POLICY pSecurityPolicy; + + ACTION_TYPE ActionType; + MATCH_TYPE MatchType; + UCHAR OperationType; + + UCHAR RuleNumber; /* is used to associate text descriptions with certain rules */ + + USHORT PolicyLineNumber; /* line number in the policy file */ + + /* + * the majority of rules use the struct below to hold information about string objects they represent + * RULE_SYSCALL though does not have any names associated with it and uses ServiceBitArray to create + * a bit index for all system calls. Both Name & ServiceBitArray are allocated dynamically. + * (in C++ we would have 2 different classes for this) + */ + union + { + struct + { + USHORT NameLength; + CHAR Name[ANYSIZE_ARRAY]; + }; + + ULONG ServiceBitArray[ANYSIZE_ARRAY]; + }; + +} POLICY_RULE, *PPOLICY_RULE; + + + +typedef struct _SECURITY_POLICY +{ + PPOLICY_RULE RuleList[RULE_LASTONE]; + + KSPIN_LOCK SpinLock; + + BOOLEAN Initialized; /* Has this policy been initialized already? */ + + +#define PROTECTION_OVERFLOW (1 << 0) +#define PROTECTION_USERLAND (1 << 1) +#define PROTECTION_DEBUGGING (1 << 2) +#define PROTECTION_VDM (1 << 3) +#define PROTECTION_KEYBOARD (1 << 4) +#define PROTECTION_MODEM (1 << 5) +#define PROTECTION_SNIFFER (1 << 6) +#define PROTECTION_EXTENSION (1 << 7) + + USHORT ProtectionFlags; + + ACTION_TYPE DefaultPolicyAction; + + PWSTR Name; + +} SECURITY_POLICY, *PSECURITY_POLICY; + + +#define IS_OVERFLOW_PROTECTION_ON(SecPolicy) (((SecPolicy).ProtectionFlags & PROTECTION_OVERFLOW) == PROTECTION_OVERFLOW) +#define IS_USERLAND_PROTECTION_ON(SecPolicy) (((SecPolicy).ProtectionFlags & PROTECTION_USERLAND) == PROTECTION_USERLAND) +#define IS_DEBUGGING_PROTECTION_ON(SecPolicy) (((SecPolicy).ProtectionFlags & PROTECTION_DEBUGGING) == PROTECTION_DEBUGGING) +#define IS_VDM_PROTECTION_ON(SecPolicy) (((SecPolicy).ProtectionFlags & PROTECTION_VDM) == PROTECTION_VDM) +#define IS_EXTENSION_PROTECTION_ON(SecPolicy) (((SecPolicy).ProtectionFlags & PROTECTION_EXTENSION) == PROTECTION_EXTENSION) + +#define TURN_DEBUGGING_PROTECTION_OFF(SecPolicy) ((SecPolicy).ProtectionFlags &= ~PROTECTION_DEBUGGING) +#define TURN_VDM_PROTECTION_OFF(SecPolicy) ((SecPolicy).ProtectionFlags &= ~PROTECTION_VDM) +#define TURN_EXTENSION_PROTECTION_OFF(SecPolicy) ((SecPolicy).ProtectionFlags &= ~PROTECTION_EXTENSION) + + +#define PROTECTION_ALL_ON 0xFFFF +#define PROTECTION_ALL_OFF 0x0000 + +#define INVALID_OBJECT_SIZE (-1) + + +extern SECURITY_POLICY gSecPolicy; +extern CHAR SystemDrive, SystemRoot[], SystemRootUnresolved[], *SystemRootDirectory, CDrive[]; +extern USHORT SystemRootLength, SystemRootUnresolvedLength, SystemRootDirectoryLength, CDriveLength; +extern ULONG NumberOfBitsInUlong, UlongBitShift; + + +#define WILDCARD_MATCH 1 +#define WILDCARD_NO_MATCH 0 + + +BOOLEAN InitPolicy(); +void PolicyRemove(); +void PolicyDelete(IN PSECURITY_POLICY pSecPolicy); +BOOLEAN LoadSecurityPolicy(OUT PSECURITY_POLICY pSecPolicy, IN PWSTR PolicyFile, IN PWSTR FilePath); +BOOLEAN FindAndLoadSecurityPolicy(OUT PSECURITY_POLICY pSecPolicy, IN PWSTR filename, IN PWSTR UserName); +ACTION_TYPE PolicyCheck(RULE_TYPE RuleType, PCHAR Object, UCHAR OperationType, UCHAR *RuleNumber, PWSTR *PolicyFilename, USHORT *PolicyLineNumber); +BOOLEAN PolicyParseObjectRule(PSECURITY_POLICY pSecPolicy, RULE_TYPE RuleType, PCHAR Operation, PCHAR rule); +VOID InsertPolicyRule(PSECURITY_POLICY pSecPolicy, PPOLICY_RULE PolicyRule, RULE_TYPE RuleType); +BOOLEAN PolicyPostBootup(); +int WildcardMatch(PCHAR path, PCHAR regex); + + +#endif /* __POLICY_H__ */ diff --git a/port.c b/port.c new file mode 100644 index 0000000..bd99908 --- /dev/null +++ b/port.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * port.c + * + * Abstract: + * + * This module implements various port object hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include "port.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitPortHooks) +#endif + + +fpZwCreatePort OriginalNtCreatePort = NULL; +fpZwCreateWaitablePort OriginalNtCreateWaitablePort = NULL; + +fpZwConnectPort OriginalNtConnectPort = NULL; +fpZwSecureConnectPort OriginalNtSecureConnectPort = NULL; + + +/* + * HookedNtCreatePort() + * + * Description: + * This function mediates the NtCreatePort() system service and checks the + * provided port name against the global and current process security policies. + * + * NOTE: ZwCreatePort creates a port object. [NAR] + * + * Parameters: + * Those of NtCreatePort(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreatePort(). + */ + +NTSTATUS +NTAPI +HookedNtCreatePort +( + OUT PHANDLE PortHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG MaxDataSize, + IN ULONG MaxMessageSize, + IN ULONG Reserved +) +{ + PCHAR FunctionName = "HookedNtCreatePort"; + + + HOOK_ROUTINE_START_OPTYPE(PORT, OP_PORT_CREATE); + + + ASSERT(OriginalNtCreatePort); + + rc = OriginalNtCreatePort(PortHandle, ObjectAttributes, MaxDataSize, MaxMessageSize, Reserved); + + + HOOK_ROUTINE_FINISH_OPTYPE(PORT, OP_PORT_CREATE); +} + + + +/* + * HookedNtCreateWaitablePort() + * + * Description: + * This function mediates the NtCreateWaitablePort() system service and checks the + * provided port name against the global and current process security policies. + * + * NOTE: ZwCreateWaitablePort creates a waitable port object. [NAR] + * + * Parameters: + * Those of NtCreateWaitablePort(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateWaitablePort(). + */ + +NTSTATUS +NTAPI +HookedNtCreateWaitablePort +( + OUT PHANDLE PortHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG MaxDataSize, + IN ULONG MaxMessageSize, + IN ULONG Reserved +) +{ + PCHAR FunctionName = "HookedNtCreateWaitablePort"; + + + HOOK_ROUTINE_START_OPTYPE(PORT, OP_PORT_CREATE); + + + ASSERT(OriginalNtCreateWaitablePort); + + rc = OriginalNtCreateWaitablePort(PortHandle, ObjectAttributes, MaxDataSize, MaxMessageSize, Reserved); + + + HOOK_ROUTINE_FINISH_OPTYPE(PORT, OP_PORT_CREATE); +} + + + +/* + * HookedNtConnectPort() + * + * Description: + * This function mediates the NtConnectPort() system service and checks the + * provided port name against the global and current process security policies. + * + * NOTE: ZwConnectPort creates a port connected to a named port. [NAR] + * + * Parameters: + * Those of NtConnectPort(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtConnectPort(). + */ + +NTSTATUS +NTAPI +HookedNtConnectPort +( + OUT PHANDLE PortHandle, + IN PUNICODE_STRING PortName, + IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, + IN OUT PPORT_SECTION_WRITE WriteSection OPTIONAL, + IN OUT PPORT_SECTION_READ ReadSection OPTIONAL, + OUT PULONG MaxMessageSize OPTIONAL, + IN OUT PVOID ConnectData OPTIONAL, + IN OUT PULONG ConnectDataLength OPTIONAL +) +{ + PCHAR FunctionName = "HookedNtConnectPort"; + UNICODE_STRING usInputPortName; + CHAR PORTNAME[MAX_PATH]; + ANSI_STRING asPortName; + + + HOOK_ROUTINE_ENTER(); + + + if (!VerifyUnicodeString(PortName, &usInputPortName)) + { + LOG(LOG_SS_PORT, LOG_PRIORITY_DEBUG, ("HookedNtConnectPort: VerifyUnicodeString failed\n")); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + if (_snprintf(PORTNAME, MAX_PATH, "%S", usInputPortName.Buffer) < 0) + { + LOG(LOG_SS_PORT, LOG_PRIORITY_DEBUG, ("%s: Port name '%S' is too long\n", FunctionName, usInputPortName.Buffer)); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(PORT, OP_PORT_CONNECT); + } + + + ASSERT(OriginalNtConnectPort); + + rc = OriginalNtConnectPort(PortHandle, PortName, SecurityQos, WriteSection, ReadSection, MaxMessageSize, + ConnectData, ConnectDataLength); + + + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(PORT, PORTNAME, OP_PORT_CONNECT); +} + + + +/* + * HookedNtSecureConnectPort() + * + * Description: + * This function mediates the NtSecureConnectPort() system service and checks the + * provided port name against the global and current process security policies. + * + * NOTE: ZwSecureConnectPort creates a port connected to a named port. [NAR] + * + * Parameters: + * Those of NtSecureConnectPort(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtSecureConnectPort(). + */ + +NTSTATUS +NTAPI +HookedNtSecureConnectPort +( + OUT PHANDLE PortHandle, + IN PUNICODE_STRING PortName, + IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, + IN OUT PPORT_SECTION_WRITE WriteSection OPTIONAL, + IN PSID ServerSid OPTIONAL, + IN OUT PPORT_SECTION_READ ReadSection OPTIONAL, + OUT PULONG MaxMessageSize OPTIONAL, + IN OUT PVOID ConnectData OPTIONAL, + IN OUT PULONG ConnectDataLength OPTIONAL +) +{ + PCHAR FunctionName = "HookedNtSecureConnectPort"; + UNICODE_STRING usInputPortName; + CHAR PORTNAME[MAX_PATH]; + ANSI_STRING asPortName; + + + HOOK_ROUTINE_ENTER(); + + + if (!VerifyUnicodeString(PortName, &usInputPortName)) + { + LOG(LOG_SS_PORT, LOG_PRIORITY_DEBUG, ("HookedNtSecureConnectPort: VerifyUnicodeString failed\n")); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + asPortName.Length = 0; + asPortName.MaximumLength = MAX_PATH - 1; + asPortName.Buffer = PORTNAME; + + if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&asPortName, &usInputPortName, FALSE))) + { + LOG(LOG_SS_PORT, LOG_PRIORITY_DEBUG, ("HookedNtSecureConnectPort: RtlUnicodeStringToAnsiString failed\n")); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + PORTNAME[asPortName.Length] = 0; + + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(PORT, OP_PORT_CONNECT); + } + + + ASSERT(OriginalNtSecureConnectPort); + + rc = OriginalNtSecureConnectPort(PortHandle, PortName, SecurityQos, WriteSection, ServerSid, ReadSection, + MaxMessageSize, ConnectData, ConnectDataLength); + + + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(PORT, PORTNAME, OP_PORT_CONNECT); +} + + + +/* + * InitPortHooks() + * + * Description: + * Initializes all the mediated port operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitPortHooks() +{ + if ( (OriginalNtCreatePort = (fpZwCreatePort) ZwCalls[ZW_CREATE_PORT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_PORT, LOG_PRIORITY_DEBUG, ("InitPortHooks: OriginalNtCreatePort is NULL\n")); + return FALSE; + } + + if ( (OriginalNtCreateWaitablePort = (fpZwCreateWaitablePort) ZwCalls[ZW_CREATE_WAITPORT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_PORT, LOG_PRIORITY_DEBUG, ("InitPortHooks: OriginalNtCreateWaitablePort is NULL\n")); + return FALSE; + } + + if ( (OriginalNtConnectPort = (fpZwConnectPort) ZwCalls[ZW_CONNECT_PORT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_PORT, LOG_PRIORITY_DEBUG, ("InitPortHooks: OriginalNtConnectPort is NULL\n")); + return FALSE; + } + + if ( (OriginalNtSecureConnectPort = (fpZwSecureConnectPort) ZwCalls[ZW_SECURECONNECT_PORT_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_PORT, LOG_PRIORITY_DEBUG, ("InitPortHooks: OriginalNtSecureConnectPort is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/port.h b/port.h new file mode 100644 index 0000000..dfa5747 --- /dev/null +++ b/port.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * port.h + * + * Abstract: + * + * This module defines various types used by port object hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __PORT_H__ +#define __PORT_H__ + + +#include +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" +#include "log.h" + + +/* + * ZwCreatePort creates a port object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreatePort) ( + OUT PHANDLE PortHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG MaxDataSize, + IN ULONG MaxMessageSize, + IN ULONG Reserved + ); + +NTSTATUS +NTAPI +HookedNtCreatePort( + OUT PHANDLE PortHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG MaxDataSize, + IN ULONG MaxMessageSize, + IN ULONG Reserved + ); + + +/* + * ZwCreateWaitablePort creates a waitable port object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateWaitablePort) ( + OUT PHANDLE PortHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG MaxDataSize, + IN ULONG MaxMessageSize, + IN ULONG Reserved + ); + +NTSTATUS +NTAPI +HookedNtCreateWaitablePort( + OUT PHANDLE PortHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG MaxDataSize, + IN ULONG MaxMessageSize, + IN ULONG Reserved + ); + + + +typedef struct _PORT_SECTION_READ { + ULONG Length; + ULONG ViewSize; + ULONG ViewBase; +} PORT_SECTION_READ, *PPORT_SECTION_READ; + +typedef struct _PORT_SECTION_WRITE { + ULONG Length; + HANDLE SectionHandle; + ULONG SectionOffset; + ULONG ViewSize; + PVOID ViewBase; + PVOID TargetViewBase; +} PORT_SECTION_WRITE, *PPORT_SECTION_WRITE; + + +/* + * ZwConnectPort creates a port connected to a named port. [NAR] + */ + +typedef NTSTATUS (*fpZwConnectPort) ( + OUT PHANDLE PortHandle, + IN PUNICODE_STRING PortName, + IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, + IN OUT PPORT_SECTION_WRITE WriteSection OPTIONAL, + IN OUT PPORT_SECTION_READ ReadSection OPTIONAL, + OUT PULONG MaxMessageSize OPTIONAL, + IN OUT PVOID ConnectData OPTIONAL, + IN OUT PULONG ConnectDataLength OPTIONAL + ); + +NTSTATUS +NTAPI +HookedNtConnectPort( + OUT PHANDLE PortHandle, + IN PUNICODE_STRING PortName, + IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, + IN OUT PPORT_SECTION_WRITE WriteSection OPTIONAL, + IN OUT PPORT_SECTION_READ ReadSection OPTIONAL, + OUT PULONG MaxMessageSize OPTIONAL, + IN OUT PVOID ConnectData OPTIONAL, + IN OUT PULONG ConnectDataLength OPTIONAL + ); + + +/* + * ZwSecureConnectPort creates a port connected to a named port. [NAR] + */ + +typedef NTSTATUS (*fpZwSecureConnectPort) ( + OUT PHANDLE PortHandle, + IN PUNICODE_STRING PortName, + IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, + IN OUT PPORT_SECTION_WRITE WriteSection OPTIONAL, + IN PSID ServerSid OPTIONAL, + IN OUT PPORT_SECTION_READ ReadSection OPTIONAL, + OUT PULONG MaxMessageSize OPTIONAL, + IN OUT PVOID ConnectData OPTIONAL, + IN OUT PULONG ConnectDataLength OPTIONAL + ); + +NTSTATUS +NTAPI +HookedNtSecureConnectPort( + OUT PHANDLE PortHandle, + IN PUNICODE_STRING PortName, + IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, + IN OUT PPORT_SECTION_WRITE WriteSection OPTIONAL, + IN PSID ServerSid OPTIONAL, + IN OUT PPORT_SECTION_READ ReadSection OPTIONAL, + OUT PULONG MaxMessageSize OPTIONAL, + IN OUT PVOID ConnectData OPTIONAL, + IN OUT PULONG ConnectDataLength OPTIONAL + ); + + + +BOOLEAN InitPortHooks(); + + +#endif /* __PORT_H__ */ diff --git a/process.c b/process.c new file mode 100644 index 0000000..c83603f --- /dev/null +++ b/process.c @@ -0,0 +1,1270 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * process.c + * + * Abstract: + * + * This module defines various types used by process and thread hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 23-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "process.h" +#include "driver.h" +#include "policy.h" +#include "hookproc.h" +#include "userland.h" +#include "procname.h" +#include "accessmask.h" +#include "learn.h" +#include "misc.h" +#include "i386.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitProcessEntries) +#pragma alloc_text (PAGE, ProcessPostBootup) +#endif + + +fpZwCreateProcess OriginalNtCreateProcess = NULL; +fpZwCreateProcessEx OriginalNtCreateProcessEx = NULL; +fpZwOpenProcess OriginalNtOpenProcess = NULL; + +fpZwCreateThread OriginalNtCreateThread = NULL; +fpZwOpenThread OriginalNtOpenThread = NULL; + + +BOOLEAN AllowProcessesWithPoliciesOnly = FALSE; + +WCHAR OzoneInstallPath[MAX_PATH]; +USHORT OzoneInstallPathSize = 0; + + +/* XXX this will not work on 64-bit architectures, due to assumption of size of ulong, pointers, etc */ +typedef struct _CONTROL_AREA { // must be quadword sized. + ULONG data[9]; + PFILE_OBJECT FilePointer; +} CONTROL_AREA, *PCONTROL_AREA; + +typedef struct _SEGMENT { + struct _CONTROL_AREA *ControlArea; +} SEGMENT, *PSEGMENT; + +typedef struct _SECTION { + ULONG_PTR data[5]; + PSEGMENT Segment; +} SECTION, *PSECTION; + + + +/* + * rand() + * + * Description: + * Returns a random number that is calculated by XOR'ing together often changing system values. + * + * Parameters: + * randval - An optional random value. + * + * Returns: + * A random ULONG value. + */ + +ULONG +rand(ULONG randval) +{ + LARGE_INTEGER perf, TickCount; + ULONG r; + + + KeQueryPerformanceCounter(&perf); +// KdPrint(("perf: %d %d\n", perf.LowPart, perf.HighPart)); + + KeQueryTickCount(&TickCount); +// KdPrint(("tick: %d %d\n", TickCount.LowPart, TickCount.HighPart)); + +// KdPrint(("int: %I64d\n", KeQueryInterruptTime())); + + r = randval ^ + perf.LowPart ^ perf.HighPart ^ + TickCount.HighPart ^ TickCount.LowPart ^ + (ULONG) KeQueryInterruptTime(); + +// KdPrint(("rand = %x\n", r)); + + + return r; +} + + + +/* + * PostProcessNtCreateProcess() + * + * Description: + * This function is called by the mediated NtCreateProcess() system service. + * It is responsible for saving the information about the newly created process in a + * global process hash table. + * + * Parameters: + * ProcessHandle - process object handle initialized by NtCreateProcess(). + * SectionHandle - specifies a handle to an image section that grants SECTION_MAP_EXECUTE + * access. If this value is zero, the new process inherits the address space from the process + * referred to by InheritFromProcessHandle. In Windows 2000 the lowest bit specifies + * (when set) that the process should not be associated with the job of the + * InheritFromProcessHandle process. + * + * Returns: + * ULONG indicating an action to take (ACTION_DENY to disallow process createion, ACTION_PERMIT to allow). + */ + +ULONG +PostProcessNtCreateProcess(PHANDLE ProcessHandle, HANDLE SectionHandle) +{ + NTSTATUS status, rc; + PIMAGE_PID_ENTRY p, prev, NewProcess; + ULONG ProcessId, Size; + PULONG_PTR Base; + UNICODE_STRING usPath; + ANSI_STRING asPath; + KIRQL irql; + PROCESS_BASIC_INFORMATION ProcessBasicInfo; + CHAR ProcessPathUnresolved[MAX_PATH]; + CHAR PROCESSNAME[MAX_PATH]; + PCHAR FunctionName = "PostProcessNtCreateProcess"; + + + if (ProcessHandle == NULL) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_WARNING, ("%s: ProcessHandle is NULL\n", FunctionName)); + return ACTION_NONE; + } + + + /* + * Try to retrieve the image name from a section object + */ + + if (!SectionHandle) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_WARNING, ("%s: SectionHandle is NULL\n", FunctionName)); + return ACTION_NONE; + } + + + do + { + PSECTION SectionToMap; + PSEGMENT seg; + PCONTROL_AREA pca; + PFILE_OBJECT pfo; + + + status = ObReferenceObjectByHandle( + SectionHandle, + 0, + 0, + KernelMode, + (PSECTION *) &SectionToMap, + NULL + ); + +/* macro shortcut for bailing out of PostProcessNtCreateProcess do {} while loop in case of an error */ + +#define ABORT_PostProcessNtCreateProcess(msg) \ + { \ + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("Error occurred in %s:", FunctionName)); \ + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, (msg)); \ + return ACTION_NONE; /* XXX */ \ + } + + if (! NT_SUCCESS(status)) + + ABORT_PostProcessNtCreateProcess(("ObReferenceObjectByHandle(SectionHandle) failed")); + + + if (SectionToMap == NULL) + + ABORT_PostProcessNtCreateProcess(("SectionToMap is NULL")); + + + if ( (seg = ((PSECTION)SectionToMap)->Segment) == NULL) + + ABORT_PostProcessNtCreateProcess(("Segment is NULL")); + + if ( (pca = seg->ControlArea) == NULL) + + ABORT_PostProcessNtCreateProcess(("ControlArea is NULL")); + + + if ( (pfo = pca->FilePointer) == NULL) + + ABORT_PostProcessNtCreateProcess(("FilePointer is NULL")); + + + usPath = pfo->FileName; + + if (usPath.Length == 0) + + ABORT_PostProcessNtCreateProcess(("FileName length is 0")); + + + _snprintf(ProcessPathUnresolved, MAX_PATH, "%S", usPath.Buffer); + ProcessPathUnresolved[MAX_PATH - 1] = 0; + + + ObDereferenceObject(SectionToMap); + + } while (0); + + + /* + * Now verify the executable name against the security policy + */ + + VerifyExecutableName(ProcessPathUnresolved); + + + if (LearningMode == FALSE) + { + ACTION_TYPE Action; + + + VerifyUserReturnAddress(); + + + FixupFilename(ProcessPathUnresolved, PROCESSNAME, MAX_PATH); + + + /* + * We manually inc/dec HookedRoutineRunning since POLICY_CHECK_OPTYPE_NAME() can call + * HOOK_ROUTINE_EXIT() which will decrement HookedRoutineRunning and then it will get + * decremented the second time in HookedNtCreateProcess() + */ + +#if DBG + InterlockedIncrement(&HookedRoutineRunning); +#endif + + POLICY_CHECK_OPTYPE_NAME(PROCESS, OP_PROC_EXECUTE); + +#if DBG + InterlockedDecrement(&HookedRoutineRunning); +#endif + } + else + { + // learning mode + AddRule(RULE_PROCESS, ProcessPathUnresolved, OP_PROC_EXECUTE); + } + + + /* + * retrieve the Process ID + */ + + status = ZwQueryInformationProcess(*ProcessHandle, ProcessBasicInformation, &ProcessBasicInfo, sizeof(ProcessBasicInfo), &Size); + + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: ZwQueryInformationProcess failed with status %x\n", status)); + return ACTION_NONE; + } + + ProcessId = ProcessBasicInfo.UniqueProcessId; + + /* + * On win2k ProcessId is not available at this stage yet. + * ProcessId of 0 will get replaced by a real value in CreateProcessNotifyProc. + */ + + + /* once winlogon.exe executes, we consider the boot process to be complete */ + if (BootingUp == TRUE) + { + PCHAR ProcessName; + + + ProcessName = strrchr(ProcessPathUnresolved, '\\'); + + if (ProcessName == NULL) + ProcessName = ProcessPathUnresolved; + else + ++ProcessName; /* skip past the slash */ + + if (_strnicmp(ProcessName, "winlogon.exe", 12) == 0) + { + BootingUp = FALSE; + InitPostBootup(); + } + } + + + /* + * Now create a new process entry and load the associated security policy (if any) + */ + + NewProcess = CreateNewProcessEntry(ProcessId, CURRENT_PROCESS_PID, &usPath, TRUE); + + + if (ProcessInsertImagePidEntry(ProcessId, NewProcess) == FALSE) + { + ExFreePoolWithTag(NewProcess, _POOL_TAG); + + return ACTION_NONE; + } + + + /* + * Now find and load appropriate security policy. + * + * Look for a per-user policy first. To do that, we send an SID resolve request + * to userland Ozone Agent Service. + */ + + if (LearningMode == FALSE) + { + PSID_RESOLVE_REPLY pSidResolveReply = NULL; + PWSTR UserName = NULL; + + + if (IssueUserlandSidResolveRequest(NewProcess) != FALSE) + { + if (NewProcess->UserlandReply) + { + pSidResolveReply = (PSID_RESOLVE_REPLY) NewProcess->UserlandReply; + + if (pSidResolveReply->UserNameLength) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: SID resolved to %S\n", pSidResolveReply->UserName)); + UserName = pSidResolveReply->UserName; + } + else + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess(): SID resolve error\n")); + } + } + } + + + if (! FindAndLoadSecurityPolicy(&NewProcess->SecPolicy, usPath.Buffer, UserName)) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_WARNING, ("%d PostProcessNtCreateProcess(): no policy, %d, %S\n", CURRENT_PROCESS_PID, ProcessId, usPath.Buffer)); + + //XXX have an option where only processes with existing policies (even if they are empty.. policy_default: allow) are allowed to run + //interactive session is excluded?!?! + if (AllowProcessesWithPoliciesOnly == TRUE) + { + ExFreePoolWithTag(NewProcess, _POOL_TAG); + return ACTION_DENY; + } + } + else + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_WARNING, ("%d PostProcessNtCreateProcess(): with policy, %d, %S\n", CURRENT_PROCESS_PID, ProcessId, usPath.Buffer)); + } + + + if (NewProcess->UserlandReply) + { + ExFreePoolWithTag(NewProcess->UserlandReply, _POOL_TAG); + NewProcess->UserlandReply = NULL; + } + } + + + /* + * Stack Buffer Overflow Protection (Part 1). + * + * 1. Allocate/reserve a random (< 0x4000000) chunk of virtual memory starting at 0xFFFF. + * This causes the stack of the main thread to be moved by a random amount. + * + * (note1: first 64K of memory are allocated as a guard against NULL pointers dereferencing). + * (note2: main() is mapped at 0x4000000 and thus we cannot allocate anything that will cause the + * stack of the main thread to move past the 0x400000 boundary). + * + * + * Stack Buffer Overflow Protection (Part 2). + * + * 2. Allocate a random (> 4 Megs && < 10 Megs) chunk of virtual memory after the process code segment. + * This causes the heap and stacks non-main threads to be moved by a random amount. + * + * (note: as mentioned above, main() is mapped at 0x4000000, by reserving a large chunk of virtual + * we force Windows to find the first available address beyond the code segment and reserve + * a random amount of memory past it causing other thread stacks and heaps to shift). + */ + +#define ONE_MEGABYTE (1024 * 1024) + + if (IS_OVERFLOW_PROTECTION_ON(gSecPolicy) && IS_OVERFLOW_PROTECTION_ON(NewProcess->SecPolicy) && LearningMode == FALSE && BootingUp == FALSE) + { +//XXX verify that the image entry is actually at 4 meg (not true for most of system processes) + + /* + * allocate up to 3 megs of virtual address space before the code segment, + * this affects main thread stack as well as some heaps + */ + +#define FIRST_AVAILABLE_ADDRESS 0xFFFF + + Size = PAGE_SIZE + (rand(ProcessId) % (3 * ONE_MEGABYTE)); + Base = (PULONG_PTR) FIRST_AVAILABLE_ADDRESS; + + status = ZwAllocateVirtualMemory(*ProcessHandle, &Base, 0L, &Size, MEM_RESERVE, PAGE_NOACCESS); + + if (! NT_SUCCESS(status)) + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: ZwAllocateVirtualMemory1(%x, %x) failed with status %x\n", Base, Size, status)); + else + LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("PostProcessNtCreateProcess: size=%u base=%x status=%d\n", Size, Base, status)); + + /* + * allocate up to 10 megs of virtual address space after the code segment, + * this affects non-main thread stack as well as some heaps + */ + +#define IMAGE_BASE (4 * ONE_MEGABYTE) + + Size = IMAGE_BASE + (rand(ProcessId) % (10 * ONE_MEGABYTE)); + Base = (PULONG_PTR) NULL; + + status = ZwAllocateVirtualMemory(*ProcessHandle, &Base, 0L, &Size, MEM_RESERVE, PAGE_NOACCESS); + + LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("PostProcessNtCreateProcess: size=%u base=%x status=%d\n", Size, Base, status)); + + if (! NT_SUCCESS(status)) + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: ZwAllocateVirtualMemory2(%x, %x) failed with status %x\n", Base, Size, status)); + + + + /* + * allocate the entire KnownDll space + */ +//#if 0 +// Size = (4 * ONE_MEGABYTE) + (rand(ProcessId) % (100 * ONE_MEGABYTE)); +// Base = (PULONG_PTR) 0x71bf0000; + +// Size = 0x7000000;//(125 * ONE_MEGABYTE); +// Base = (PULONG_PTR) 0x70000000; + +#if HOOK_BOPROT + if (strstr(ProcessPathUnresolved, "stack.exe") != NULL) + { + Size = PAGE_SIZE; + // Base = (PULONG_PTR) 0x77d00000; //user32 + Base = (PULONG_PTR) 0x77e30000; //kernel32 on win2k3 +// Base = (PULONG_PTR) 0x77e80000; //kernel32 on win2k + + status = ZwAllocateVirtualMemory(*ProcessHandle, &Base, 0L, &Size, MEM_RESERVE, PAGE_NOACCESS); + + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: kernel32.dll size=%u base=%x status=%d\n", Size, Base, status)); + + if (! NT_SUCCESS(status)) + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: ZwAllocateVirtualMemory1(%x, %x) failed with status %x\n", Base, Size, status)); + } + else + NewProcess->FirstThread = FALSE;//XXX remove +#endif + } + + + return ACTION_PERMIT; +} + + + +/* + * HookedNtCreateProcess() + * + * Description: + * This function mediates the NtCreateProcess() system service in order to keep track of all + * the newly created processes. + * + * NOTE: ZwCreateProcess creates a process object. [NAR] + * + * Parameters: + * Those of NtCreateProcess(). + * + * Returns: + * NTSTATUS returned by NtCreateProcess(). + */ + +NTSTATUS +NTAPI +HookedNtCreateProcess +( + OUT PHANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN HANDLE InheritFromProcessHandle, + IN BOOLEAN InheritHandles, + IN HANDLE SectionHandle OPTIONAL, + IN HANDLE DebugPort OPTIONAL, + IN HANDLE ExceptionPort OPTIONAL +) +{ + HOOK_ROUTINE_ENTER(); + + + ASSERT(OriginalNtCreateProcess); + + rc = OriginalNtCreateProcess(ProcessHandle, DesiredAccess, ObjectAttributes, InheritFromProcessHandle, + InheritHandles, SectionHandle, DebugPort, ExceptionPort); + + + if (NT_SUCCESS(rc)) + { + ULONG ret = PostProcessNtCreateProcess(ProcessHandle, SectionHandle); + + if (ret == ACTION_DENY || ret == STATUS_ACCESS_DENIED) + { + ZwClose(*ProcessHandle); + rc = STATUS_ACCESS_DENIED; + } + } + + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * HookedNtCreateProcessEx() + * + * Description: + * This function mediates the NtCreateProcessEx() system service in order to keep track of all + * the newly created processes. + * + * NOTE: ZwCreateProcessEx creates a process object. [NAR] + * + * Parameters: + * Those of NtCreateProcessEx(). + * + * Returns: + * NTSTATUS returned by NtCreateProcessEx(). + */ + +NTSTATUS +NTAPI +HookedNtCreateProcessEx +( + OUT PHANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN HANDLE InheritFromProcessHandle, + IN ULONG Unknown1, + IN HANDLE SectionHandle OPTIONAL, + IN HANDLE DebugPort OPTIONAL, + IN HANDLE ExceptionPort OPTIONAL, + IN ULONG Unknown2 +) +{ + HOOK_ROUTINE_ENTER(); + + + ASSERT(OriginalNtCreateProcessEx); + + rc = OriginalNtCreateProcessEx(ProcessHandle, DesiredAccess, ObjectAttributes, InheritFromProcessHandle, + Unknown1, SectionHandle, DebugPort, ExceptionPort, Unknown2); + + + if (NT_SUCCESS(rc)) + { + ULONG ret = PostProcessNtCreateProcess(ProcessHandle, SectionHandle); + + if (ret == ACTION_DENY || ret == STATUS_ACCESS_DENIED) + { + ZwClose(*ProcessHandle); + rc = STATUS_ACCESS_DENIED; + } + } + + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * HookedNtOpenProcess() + * + * Description: + * This function mediates the NtOpenProcess() system service and disallows certain operations such as + * PROCESS_VM_WRITE and PROCESS_CREATE_THREAD. + * + * NOTE: ZwOpenProcess opens a process object. [NAR] + * + * Parameters: + * Those of NtOpenProcess(). + * + * Returns: + * NTSTATUS returned by NtOpenProcess(). + */ + +NTSTATUS +NTAPI +HookedNtOpenProcess +( + OUT PHANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PCLIENT_ID ClientId OPTIONAL +) +{ + PCHAR FunctionName = "HookedNtOpenProcess"; + CHAR PROCESSNAME[MAX_PATH]; + + +//taskmgr uses PROCESS_TERMINATE to kill processes +//XXX IPD disallows PROCESS_CREATE_THREAD|PROCESS_VM_WRITE + +/* +PROCESS_TERMINATE Terminate process +PROCESS_CREATE_THREAD Create threads in process +PROCESS_SET_SESSIONID Set process session id +PROCESS_VM_OPERATION Protect and lock memory of process +PROCESS_VM_READ Read memory of process +PROCESS_VM_WRITE Write memory of process +PROCESS_DUP_HANDLE Duplicate handles of process +PROCESS_CREATE_PROCESS Bequeath address space and handles to new process +PROCESS_SET_QUOTA Set process quotas +PROCESS_SET_INFORMATION Set information about process +PROCESS_QUERY_INFORMATION Query information about process +PROCESS_SET_PORT Set process exception or debug port +PROCESS_ALL_ACCESS All of the preceding + +find out who uses which flags, i.e. VM_READ, etc.. filter out accordingly +*/ + + HOOK_ROUTINE_ENTER(); + + +// if (! IS_BIT_SET(DesiredAccess, PROCESS_QUERY_INFORMATION) && +// ! IS_BIT_SET(DesiredAccess, PROCESS_DUP_HANDLE) && +// ! IS_BIT_SET(DesiredAccess, SYNCHRONIZE) ) + + + if (! ARGUMENT_PRESENT(ClientId) || KeGetPreviousMode() != UserMode || KeGetCurrentIrql() != PASSIVE_LEVEL) + goto done; + + + __try + { + ProbeForRead(ClientId, sizeof(*ClientId), sizeof(PULONG_PTR)); + } + + __except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%s: caught an exception. address=%x, status = 0x%x\n", FunctionName, ClientId, status)); + + goto done; + } + + + if (PsGetCurrentProcessId() != ClientId->UniqueProcess && + (ULONG) PsGetCurrentProcessId() != SystemProcessId && + ClientId->UniqueProcess != 0) + { + PIMAGE_PID_ENTRY p; + + /* can't access ClientId (pageable memory) while holding spinlonk */ + ULONG RequestedProcessId = (ULONG) ClientId->UniqueProcess; + + + //XXX +/* + if (RequestedProcessId == UserAgentServicePid) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("%s: FindImagePidEntry(%d) UserAgent %x\n", FunctionName, RequestedProcessId, DesiredAccess)); + + if (IS_BIT_SET(DesiredAccess, PROCESS_TERMINATE)) + { + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + } +*/ + + + p = FindImagePidEntry(RequestedProcessId, 0); + + if (p == NULL) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s: FindImagePidEntry(%d) failed\n", CURRENT_PROCESS_PID, FunctionName, RequestedProcessId)); + goto done; + } + +/* + if (DesiredAccess & PROCESS_CREATE_THREAD) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s(%d): %x (PROCESS_CREATE_THREAD). (%S)\n", CURRENT_PROCESS_PID, FunctionName, RequestedProcessId, DesiredAccess, p->ImageName)); + } + else if (DesiredAccess & PROCESS_VM_WRITE) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s(%d): %x (PROCESS_VM_WRITE). (%S)\n", CURRENT_PROCESS_PID, FunctionName, RequestedProcessId, DesiredAccess, p->ImageName)); + } + else + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("%d %s(%d): %x. (%S)\n", CURRENT_PROCESS_PID, FunctionName, RequestedProcessId, DesiredAccess, p->ImageName)); + } +*/ + + + if (LearningMode == FALSE) + { + CHAR ProcessPathUnresolved[MAX_PATH]; + + + _snprintf(ProcessPathUnresolved, MAX_PATH, "%S", p->ImageName); + ProcessPathUnresolved[ MAX_PATH - 1 ] = 0; + + + FixupFilename(ProcessPathUnresolved, PROCESSNAME, MAX_PATH); + + + POLICY_CHECK_OPTYPE_NAME(PROCESS, OP_PROC_OPEN); + } + else + { + // learning mode + _snprintf(PROCESSNAME, MAX_PATH, "%S", p->ImageName); + PROCESSNAME[ MAX_PATH - 1 ] = 0; + + AddRule(RULE_PROCESS, PROCESSNAME, OP_PROC_OPEN); + } + } + + + if (LearningMode == FALSE && GetPathFromOA(ObjectAttributes, PROCESSNAME, MAX_PATH, RESOLVE_LINKS)) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s: %s SPECIAL CASE\n", (ULONG) PsGetCurrentProcessId(), FunctionName, PROCESSNAME)); + } + + +done: + + ASSERT(OriginalNtOpenProcess); + + rc = OriginalNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId); + + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * HookedNtCreateThread() + * + * Description: + * This function mediates the NtCreateThread() system service in order to randomize thread stack + * and inject userland dll into newly created main threads. + * + * NOTE: ZwCreateThread creates a thread in a process. [NAR] + * + * Parameters: + * Those of NtCreateThread(). + * + * Returns: + * NTSTATUS returned by NtCreateThread(). + */ + +NTSTATUS +NTAPI +HookedNtCreateThread +( + OUT PHANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN HANDLE ProcessHandle, + OUT PCLIENT_ID ClientId, + IN PCONTEXT ThreadContext, + IN PUSER_STACK UserStack, + IN BOOLEAN CreateSuspended +) +{ + PEPROCESS proc = NULL; + USHORT StackOffset = 0; + PCHAR FunctionName = "HookedNtCreateThread"; + + + HOOK_ROUTINE_ENTER(); + + + if (ARGUMENT_PRESENT(ThreadContext) && KeGetPreviousMode() == UserMode && LearningMode == FALSE && BootingUp == FALSE) + { + NTSTATUS status; + ULONG ProcessId; + PCHAR InstructionAddress; + ULONG Size; + PCHAR Base; + PROCESS_BASIC_INFORMATION ProcessBasicInfo; + ULONG ret; + PIMAGE_PID_ENTRY p; + + + VerifyUserReturnAddress(); + + + /* verify userland parameter threadcontext */ + __try + { + ProbeForRead(ThreadContext, sizeof(*ThreadContext), sizeof(ULONG)); + ProbeForWrite(ThreadContext, sizeof(*ThreadContext), sizeof(ULONG)); + } + + __except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: caught an exception. status = 0x%x\n", FunctionName, status)); + + goto done; + } + + + if (ThreadContext->Eax > SystemAddressStart) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: eax=%x > %x (SystemAddressStart)\n", FunctionName, ThreadContext->Eax, SystemAddressStart)); + goto done; + } + + + /* retrieve the Process ID */ + status = ZwQueryInformationProcess(ProcessHandle, ProcessBasicInformation, &ProcessBasicInfo, sizeof(ProcessBasicInfo), &Size); + + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: ZwQueryInformationProcess failed with status %x\n", FunctionName, status)); + goto done; + } + + ProcessId = ProcessBasicInfo.UniqueProcessId; + + /* + * if ProcessId is 0 then the pid has not been assigned yet and we are the primary thread. + * in that case pass our pid (we are still running in the context of the parent process) as the ParentId + * to make sure we find the right process (since theoretically there can be more than one process with a + * pid of 0) + */ + p = FindImagePidEntry(ProcessId, ProcessId == 0 ? CURRENT_PROCESS_PID : 0); + + if (p == NULL) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s: FindImagePidEntry(%d) failed\n", CURRENT_PROCESS_PID, FunctionName, ProcessId)); + goto done; + } + + + /* + * Stack Buffer Overflow Protection (Part 3). + * + * Allocate/reserve a random (< 1024 bytes) part of a thread's stack space. + * This causes the least significant 10 bits to be randomized as well. + * (without this, the least significant 16 bits are always the same). + */ + + /* save the stackoffset for now since we are holding a spinlock and cannot touch pageable memory */ +//XXX we are not holding the spinlock here but are still accessing various p-> fields + if (IS_OVERFLOW_PROTECTION_ON(gSecPolicy) && IS_OVERFLOW_PROTECTION_ON(p->SecPolicy)) + + StackOffset = (USHORT) (16 + (rand(ThreadContext->Eax) % 63) * 16); + + + if (! IS_USERLAND_PROTECTION_ON(gSecPolicy) || ! IS_USERLAND_PROTECTION_ON(p->SecPolicy)) + goto done; + + + /* Userland DLL needs to be loaded only once (by the first/main thread) */ + + if (p->FirstThread != TRUE) + goto done; + + p->FirstThread = FALSE; + +//XXX investigate MEM_WRITE_WATCH (supported on i386?) + + + /* + * Inject the userland DLL into the process. + * + * This is achieved by allocating 1 page of memory in the address space of a "victim" process. + * Then the LdrLoadDll(our_dll) code is written to the allocated page and victim's thread EIP + * is changed to point to our code. + * As soon as the thread executes, it will load our DLL and then jump to the original entry point. + * + * When not given explicit directions, the Microsoft linker creates each DLL with a + * preferred load address of 0x10000000. By loading our DLL first, we cause the rest of + * the application DLLs to load at a different address. + */ + + /* allocate 1 page of commited rwx memory */ + + Size = PAGE_SIZE; + Base = NULL; + + status = ZwAllocateVirtualMemory(ProcessHandle, &Base, 0L, &Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: ZwAllocateVirtualMemory(%x, %x) failed with status %x\n", FunctionName, Base, Size, status)); + goto done; + } + + + status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_ALL_ACCESS, 0, KernelMode, &proc, NULL); + + if (! NT_SUCCESS(status)) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: ObReferenceObjectByHandle(ProcessHandle) failed with status %x\n", FunctionName, status)); + goto done; + } + + + try + { + ULONG CodeAddress, DllPathAddress; + ULONG ThreadEntryPoint = ThreadContext->Eax; /* thread entry point is stored in the EAX register */ + PWSTR InjectDllName = L"ozoneusr.dll"; + USHORT InjectDllNameSize = (wcslen(InjectDllName) + 1) * sizeof(WCHAR); + + + /* + * Execute the DLL inject in the memory context of a "victim" process + */ + + KeAttachProcess(proc); + { + /* probe the memory, just in case */ + ProbeForRead(Base, PAGE_SIZE, 1); + ProbeForWrite(Base, PAGE_SIZE, 1); + + + /* + * Memory Layout used for DLL injection + * + * Byte Value + * + * 0..4 Original EIP + * 4..8 LdrLoadDll() output handle + * 8..16 LdrLoadDll() DllName UNICODE_STRING structure + * 16..? DllName.Buffer WCHAR string + * ?..? DllPath WCHAR string + * .... assembler code (to call LdrLoadDll() and then jmp to the original EIP) + */ + +#define BASE_PTR Base +#define ADDRESS_ORIGINAL_EIP (BASE_PTR + 0) +#define ADDRESS_DLL_HANDLE (BASE_PTR + 4) +#define ADDRESS_DLL_NAME (BASE_PTR + 8) + + + /* save the original thread entry point */ + * (PULONG) ADDRESS_ORIGINAL_EIP = ThreadEntryPoint; + + /* skip past eip and output handle (8 bytes) */ + InstructionAddress = ADDRESS_DLL_NAME; + + /* + * Create a UNICODE_STRING structure + */ + + * ((PUSHORT) InstructionAddress)++ = InjectDllNameSize; /* UNICODE_STRING.Length */ + * ((PUSHORT) InstructionAddress)++ = InjectDllNameSize; /* UNICODE_STRING.MaximumLength */ + + /* UNICODE_STRING.Buffer (points to unicode string directly following the buffer) */ + + * ((PULONG) InstructionAddress)++ = (ULONG) (InstructionAddress + sizeof(PWSTR)); + + + /* the actual DllName.Buffer value */ + + wcscpy((PWSTR) InstructionAddress, InjectDllName); + + InstructionAddress += InjectDllNameSize; + + + /* DllPathValue value */ + + DllPathAddress = (ULONG) InstructionAddress; + + wcscpy((PWSTR) InstructionAddress, OzoneInstallPath); + + InstructionAddress += OzoneInstallPathSize; + + + CodeAddress = (ULONG) InstructionAddress; + + + /* + * Generate code that will call LdrLoadDll and then jmp to the original entry point. + */ + + /* + * NTSTATUS + * LdrLoadDll ( + * IN PWSTR DllPath OPTIONAL, + * IN PULONG DllCharacteristics OPTIONAL, + * IN PUNICODE_STRING DllName, + * OUT PVOID *DllHandle + * ); + * + * Save LdrLoadDll parameters on stack (last to first). + */ + + ASM_PUSH(InstructionAddress, ADDRESS_DLL_HANDLE); /* DllHandle */ + ASM_PUSH(InstructionAddress, ADDRESS_DLL_NAME); /* DllName */ + ASM_PUSH(InstructionAddress, 0); /* DllCharacteristics */ +// ASM_PUSH(InstructionAddress, NULL); /* DllPath */ + ASM_PUSH(InstructionAddress, DllPathAddress); /* DllPath */ + + ASM_CALL(InstructionAddress, FindFunctionBase(NTDLL_Base, "LdrLoadDll")); + + + //XXX now clear out all the data up to this instruction + //be careful first 4 bytes are used by the next instruction + /* + mov ecx, 16 + lea edi, Base + 4 + rep stosw + */ + + ASM_JMP(InstructionAddress, Base); + } + KeDetachProcess(); + + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s: Replacing original thread entry point %x with %x\n", CURRENT_PROCESS_PID, FunctionName, ThreadContext->Eax, CodeAddress)); + + /* hijack the thread instruction pointer (saved in EAX) */ +// LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("eip %x eax %x\n", ThreadContext->Eip, ThreadContext->Eax)); + + ThreadContext->Eax = (ULONG) CodeAddress; + + ThreadContext->Eip = ThreadContext->Eax; + + ObDereferenceObject(proc); + } + + except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: caught an exception. status = 0x%x\n", FunctionName, status)); + + KeDetachProcess(); + + ObDereferenceObject(proc); + } + } + + +done: + + /* + * finally adjust the stack. + * could not have done it before since we were holding a spinlock and weren't allowed to access pageable memory + */ + + if (StackOffset) + + ThreadContext->Esp -= StackOffset; + + + ASSERT(OriginalNtCreateThread); + + rc = OriginalNtCreateThread(ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle, + ClientId, ThreadContext, UserStack, CreateSuspended); + + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * HookedNtOpenThread() + * + * Description: + * This function mediates the NtOpenThread() system service in order to XXX + * . + * + * NOTE: ZwOpenThread opens a thread object. [NAR] + * + * Parameters: + * Those of NtOpenThread(). + * + * Returns: + * NTSTATUS returned by NtOpenThread(). + */ + +NTSTATUS +NTAPI +HookedNtOpenThread +( + OUT PHANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PCLIENT_ID ClientId +) +{ + CHAR THREADNAME[MAX_PATH]; + + HOOK_ROUTINE_ENTER(); + + + if (LearningMode == FALSE && GetPathFromOA(ObjectAttributes, THREADNAME, MAX_PATH, RESOLVE_LINKS)) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d HookedNtOpenThread: %s\n", (ULONG) PsGetCurrentProcessId(), THREADNAME)); + } + + + if (! IS_BIT_SET(DesiredAccess, THREAD_QUERY_INFORMATION)) + { + // ClientId->UniqueProcess = 0 if process opens a thread in the same process (wmplayer.exe) + if (ClientId) + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d HookedNtOpenThread(%d %d): %x\n", (ULONG) PsGetCurrentProcessId(), ClientId->UniqueProcess, ClientId->UniqueThread, DesiredAccess)); + else + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d HookedNtOpenThread(): %x\n", (ULONG) PsGetCurrentProcessId(), DesiredAccess)); + } + + ASSERT(OriginalNtOpenThread); + + rc = OriginalNtOpenThread(ThreadHandle, DesiredAccess, ObjectAttributes, ClientId); + + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * ProcessPostBootup() + * + * Description: + * Find out where userland Ozone files are installed once the bootup process is complete. + * We are unable to read some parts of the registry before the bootup is complete since + * they have not been initialized yet. + * + * ProcessPostBootup() might run even before bootup is complete in order to setup up the + * default values. When ProcessPostBootup() runs again after bootup, the default values + * will be overwritten with the correct ones. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +VOID +ProcessPostBootup() +{ + if (ReadStringRegistryValueW(L"\\Registry\\Machine\\Software\\Security Architects\\Ozone Agent", L"InstallPath", OzoneInstallPath, MAX_PATH) == FALSE) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("ProcessPostBootup: Failed to open InstallPath registry key\n")); + + // use the default path + wcscpy(OzoneInstallPath, L"C:\\Program Files\\Security Architects\\Ozone Agent"); + } + + OzoneInstallPathSize = (wcslen(OzoneInstallPath) + 1) * sizeof(WCHAR); +} + + + +/* + * InitProcessEntries() + * + * Description: + * Initializes all the mediated process operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ +/* +VOID +LoadImageNotifyProc +( + IN PUNICODE_STRING FullImageName, + IN HANDLE ProcessId, // where image is mapped + IN PIMAGE_INFO ImageInfo +) +{ + if (FullImageName && ImageInfo) + { + KdPrint(("LoadImageNotifyProc: %d %S %x %x\n", ProcessId, FullImageName->Buffer, ImageInfo->ImageBase, ImageInfo->ImageSize)); + } + else + { + KdPrint(("LoadImageNotifyProc: NULL Pointer %x %x\n", FullImageName, ImageInfo)); + } +} +*/ + +BOOLEAN +InitProcessEntries() +{ +// PsSetLoadImageNotifyRoutine(LoadImageNotifyProc); + + if ( (OriginalNtCreateProcess = (fpZwCreateProcess) ZwCalls[ZW_CREATE_PROCESS_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessEntries: OriginalNtCreateProcess is NULL\n")); + return FALSE; + } + + if ( (OriginalNtCreateProcessEx = (fpZwCreateProcessEx) ZwCalls[ZW_CREATE_PROCESSEX_INDEX].OriginalFunction) == NULL) + { + /* does not exist on Win2K */ + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessEntries: OriginalNtCreateProcessEx is NULL\n")); + } + + if ( (OriginalNtOpenProcess = (fpZwOpenProcess) ZwCalls[ZW_OPEN_PROCESS_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessEntries: OriginalNtOpenProcess is NULL\n")); + return FALSE; + } + + if ( (OriginalNtCreateThread = (fpZwCreateThread) ZwCalls[ZW_CREATE_THREAD_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessEntries: OriginalNtCreateThread is NULL\n")); + return FALSE; + } + + if ( (OriginalNtOpenThread = (fpZwOpenThread) ZwCalls[ZW_OPEN_THREAD_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessEntries: OriginalNtOpenThread is NULL\n")); + return FALSE; + } + + + /* + * run ProcessPostBootup() even if we are still booting up, this way we will at least setup + * the default values. When ProcessPostBootup() runs again after bootup, the default values + * will be overwritten with the correct ones. + */ + + ProcessPostBootup(); + + + return TRUE; +} diff --git a/process.h b/process.h new file mode 100644 index 0000000..7bf4f08 --- /dev/null +++ b/process.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * process.h + * + * Abstract: + * + * This module defines various types used by process and thread hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 23-Feb-2004 + * + * Revision History: + * + * None. + */ + +#ifndef __PROCESS_H__ +#define __PROCESS_H__ + + +extern ULONG SystemProcessId; + +extern WCHAR OzoneInstallPath[]; +extern USHORT OzoneInstallPathSize; + + +/* + * ZwCreateProcess creates a process object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateProcess) ( + OUT PHANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN HANDLE InheritFromProcessHandle, + IN BOOLEAN InheritHandles, + IN HANDLE SectionHandle OPTIONAL, + IN HANDLE DebugPort OPTIONAL, + IN HANDLE ExceptionPort OPTIONAL + ); + +NTSTATUS +NTAPI +HookedNtCreateProcess( + OUT PHANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN HANDLE InheritFromProcessHandle, + IN BOOLEAN InheritHandles, + IN HANDLE SectionHandle OPTIONAL, + IN HANDLE DebugPort OPTIONAL, + IN HANDLE ExceptionPort OPTIONAL + ); + + +typedef NTSTATUS (*fpZwCreateProcessEx) ( + OUT PHANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN HANDLE InheritFromProcessHandle, + IN ULONG Unknown1, + IN HANDLE SectionHandle OPTIONAL, + IN HANDLE DebugPort OPTIONAL, + IN HANDLE ExceptionPort OPTIONAL, + IN ULONG Unknown2 + ); + +NTSTATUS +NTAPI +HookedNtCreateProcessEx( + OUT PHANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN HANDLE InheritFromProcessHandle, + IN ULONG Unknown1, + IN HANDLE SectionHandle OPTIONAL, + IN HANDLE DebugPort OPTIONAL, + IN HANDLE ExceptionPort OPTIONAL, + IN ULONG Unknown2 + ); + + +/* + * ZwOpenProcess opens a process object. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenProcess) ( + OUT PHANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PCLIENT_ID ClientId OPTIONAL + ); + +NTSTATUS +NTAPI +HookedNtOpenProcess( + OUT PHANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PCLIENT_ID ClientId OPTIONAL + ); + + +/* + * ZwCreateThread creates a thread in a process. [NAR] + */ + +typedef struct _USER_STACK { + PVOID FixedStackBase; + PVOID FixedStackLimit; + PVOID ExpandableStackBase; + PVOID ExpandableStackLimit; + PVOID ExpandableStackBottom; +} USER_STACK, *PUSER_STACK; + +typedef NTSTATUS (*fpZwCreateThread) ( + OUT PHANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN HANDLE ProcessHandle, + OUT PCLIENT_ID ClientId, + IN PCONTEXT ThreadContext, + IN PUSER_STACK UserStack, + IN BOOLEAN CreateSuspended + ); + +NTSTATUS +NTAPI +HookedNtCreateThread( + OUT PHANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN HANDLE ProcessHandle, + OUT PCLIENT_ID ClientId, + IN PCONTEXT ThreadContext, + IN PUSER_STACK UserStack, + IN BOOLEAN CreateSuspended + ); + + +/* + * ZwOpenThread opens a thread object. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenThread) ( + OUT PHANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PCLIENT_ID ClientId + ); + +NTSTATUS +NTAPI +HookedNtOpenThread( + OUT PHANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PCLIENT_ID ClientId + ); + + +/* + * ZwAllocateVirtualMemory allocates virtual memory in the user mode address range. [NAR] + */ + +NTSYSAPI +NTSTATUS +NTAPI +ZwAllocateVirtualMemory( + IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN ULONG ZeroBits, + IN OUT PULONG AllocationSize, + IN ULONG AllocationType, + IN ULONG Protect + ); + + +/* + * ZwQueryInformationProcess retrieves information about a process object. [NAR] + */ + +NTSYSAPI +NTSTATUS +NTAPI +ZwQueryInformationProcess( + IN HANDLE ProcessHandle, + IN PROCESSINFOCLASS ProcessInformationClass, + OUT PVOID ProcessInformation, + IN ULONG ProcessInformationLength, + OUT PULONG ReturnLength OPTIONAL + ); + + +VOID +KeAttachProcess( + IN /*PRKPROCESS*/ PVOID Process + ); + +VOID +KeDetachProcess ( + VOID + ); + + +BOOLEAN InitProcessEntries(); +VOID RemoveProcessEntries(); +VOID ProcessPostBootup(); + + +#endif /* __PROCESS_H__ */ \ No newline at end of file diff --git a/procname.c b/procname.c new file mode 100644 index 0000000..05ba9be --- /dev/null +++ b/procname.c @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * procname.c + * + * Abstract: + * + * This module defines various types used by process id to process name conversion routines. + * + * All processes are tracked in a global hash table. + * At startup ZwQuerySystemInformation(SystemProcessesAndThreadsInformation..) is used to + * enumerate all the existing processes. After that NtCreateProcess() is hooked and used + * to keep track of newly created processes while PsSetCreateProcessNotifyRoutine() + * callbacks are used to keep track of terminating processes. + * + * See http://www.microsoft.com/msj/0199/nerd/nerd0199.aspx for more info. + * + * Author: + * + * Eugene Tsyrklevich 23-Feb-2004 + * + * Revision History: + * + * 07-Apr-2004 ET - Copied from process.h + */ + + +#include +#include "procname.h" +#include "hookproc.h" +#include "process.h" +#include "policy.h" +#include "sysinfo.h" +#include "learn.h" +#include "misc.h" +#include "log.h" + + +void FindProcessNameOffset(); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitProcessNameEntries) +#pragma alloc_text (INIT, FindProcessNameOffset) +#pragma alloc_text (INIT, EnumerateExistingProcesses) +#pragma alloc_text (PAGE, RemoveProcessNameEntries) +#endif + + +ULONG SystemProcessId; + +/* 67 * 144 = 10 kilobytes */ +IMAGE_PID_ENTRY gImagePidHtbl[IMAGE_PID_HASHTABLE_SIZE]; + +//XXX investigate KeAcquireInStackQueuedSpinLock +KSPIN_LOCK gImagePidHtblSpinLock; + + +/* + * FindImagePidEntry() + * + * Description: + * Looks for a process entry in the global process hash table with a specified process id. + * + * Parameters: + * ProcessId - process id of the process to look for. + * + * Returns: + * Pointer to a process entry if one is found, NULL otherwise. + */ + +PIMAGE_PID_ENTRY +FindImagePidEntry(ULONG ProcessId, ULONG ParentId) +{ + PIMAGE_PID_ENTRY p; + KIRQL irql; + + +//LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("FindImagePidEntry(%d %d) 1\n", ProcessId, ParentId)); + KeAcquireSpinLock(&gImagePidHtblSpinLock, &irql); +//LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("FindImagePidEntry 2\n")); + + + p = gImagePidHtbl[(ULONG) ProcessId % IMAGE_PID_HASHTABLE_SIZE].next; + + while (p) + { + if (p->ProcessId == ProcessId) + { + if (ParentId == 0 || p->ParentId == ParentId) + { + if (ParentId != 0) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d FindImagePidEntry(%d, %d) found an entry (%d)\n", CURRENT_PROCESS_PID, ProcessId, ParentId, p->ParentId)); + } + + KeReleaseSpinLock(&gImagePidHtblSpinLock, irql); + return p; + } + + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d FindImagePidEntry: ProcessId %d clash. Parent id %d vs %d\n", CURRENT_PROCESS_PID, ProcessId, ParentId, p->ParentId)); + } + + p = p->next; + } + + + KeReleaseSpinLock(&gImagePidHtblSpinLock, irql); + + + return NULL; +} + + + +/* + * ProcessInsertImagePidEntry() + * + * Description: + * Insert the new process entry into the global process hash table. + * + * Parameters: + * ProcessId - process id of the new process. + * NewProcessEntry - the entry to insert into the hash table. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +ProcessInsertImagePidEntry(ULONG ProcessId, PIMAGE_PID_ENTRY NewProcessEntry) +{ + PIMAGE_PID_ENTRY p, prev; + KIRQL irql; + + +//LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("ProcessInsertImagePidEntry(%d %x) 1\n", ProcessId, NewProcessEntry)); + KeAcquireSpinLock(&gImagePidHtblSpinLock, &irql); +//LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("ProcessInsertImagePidEntry 2\n")); + + prev = &gImagePidHtbl[(ULONG) ProcessId % IMAGE_PID_HASHTABLE_SIZE]; + p = prev->next; + + while (p) + { + // if an entry with our ProcessId already exists, bail out + + if (p->ProcessId == (ULONG) ProcessId) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d ProcessInsertImagePidEntry: ProcessId (%d) clash. New image name is '%S' (%d). Old image name is '%S' (%d)\n", CURRENT_PROCESS_PID, ProcessId, NewProcessEntry->ImageName, NewProcessEntry->ParentId, p->ImageName, p->ParentId)); + + if (ProcessId != 0) + { + KeReleaseSpinLock(&gImagePidHtblSpinLock, irql); + return FALSE; + } + } + + prev = p; + p = p->next; + } + + prev->next = NewProcessEntry; + + + KeReleaseSpinLock(&gImagePidHtblSpinLock, irql); + + + return TRUE; +} + + + +/* + * CreateNewProcessEntry() + * + * Description: + * Allocates and initializes a new process entry. + * + * Parameters: + * ProcessId - process id of the new process. + * ProcessName - process image name. + * + * Returns: + * Pointer to a heap allocated IMAGE_PID_ENTRY structure. NULL if failed. + */ + +PIMAGE_PID_ENTRY +CreateNewProcessEntry(ULONG ProcessId, ULONG ParentId, PUNICODE_STRING ProcessName, BOOLEAN NewProcess) +{ + PIMAGE_PID_ENTRY ProcessEntry; + + + ASSERT(ProcessName); + + + ProcessEntry = ExAllocatePoolWithTag(NonPagedPool, sizeof(IMAGE_PID_ENTRY) + ProcessName->Length, _POOL_TAG); + if (ProcessEntry == NULL) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("CreateNewProcessEntry: Out of memory. Forgeting about process %d (%S)", ProcessId, ProcessName->Buffer)); + return NULL; + } + + RtlZeroMemory(ProcessEntry, sizeof(IMAGE_PID_ENTRY)); + + ProcessEntry->ProcessId = ProcessId; + ProcessEntry->ParentId = ParentId; + ProcessEntry->FirstThread = NewProcess; + + KeInitializeEvent(&ProcessEntry->UserlandRequestDoneEvent, SynchronizationEvent, FALSE); + + wcscpy(ProcessEntry->ImageName, ProcessName->Buffer); + ProcessEntry->ImageName[ ProcessName->Length/sizeof(WCHAR) ] = L'\0'; + + KeInitializeSpinLock(&ProcessEntry->SecPolicy.SpinLock); + + + return ProcessEntry; +} + + + +/* + * CreateAndLoadNewProcessEntry() + * + * Description: + * Creates a new process entry and inserts it into the global process hash table. + * + * Parameters: + * ProcessId - process id of the new process. + * ProcessName - process image name. + * + * Returns: + * Pointer to a heap allocated IMAGE_PID_ENTRY structure. NULL if failed. + */ + +PIMAGE_PID_ENTRY +CreateAndLoadNewProcessEntry(ULONG ProcessId, PUNICODE_STRING ProcessName, BOOLEAN NewProcess) +{ + PIMAGE_PID_ENTRY ProcessEntry; + + + ASSERT(ProcessName); + + + ProcessEntry = CreateNewProcessEntry(ProcessId, 0, ProcessName, NewProcess); + if (ProcessEntry == NULL) + return NULL; + + + if (LearningMode == FALSE && FindAndLoadSecurityPolicy(&ProcessEntry->SecPolicy, ProcessName->Buffer, NULL) == FALSE) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("CreateAndLoadNewProcessEntry: Failed to load security policy for %S\n", ProcessName->Buffer)); + } + + + if (ProcessInsertImagePidEntry(ProcessId, ProcessEntry) == FALSE) + { + ExFreePoolWithTag(ProcessEntry, _POOL_TAG); + return NULL; + } + + + return ProcessEntry; +} + + + +/* + * CreateProcessNotifyProc() + * + * Description: + * PsSetCreateProcessNotifyRoutine() callback. Used to remove process entries from the global + * hashtable of currently running processes. On Windows 2000, this routine also replaces PIDs + * of 0 with the real PIDs. This is necessary since win2k does not assign PIDs at a point + * where process.c!PostProcessNtCreateProcess() is called. + * + * NOTE: PsSetCreateProcessNotifyRoutine() adds a driver-supplied callback routine to, + * or removes it from, a list of routines to be called whenever a process is created or deleted. + * + * Parameters: + * ParentId - the parent process ID. + * ProcessId - process ID that was created / deleted. + * Create - indicates whether the process was created (TRUE) or deleted (FALSE). + * + * Returns: + * Nothing. + */ + +VOID +CreateProcessNotifyProc +( + IN HANDLE ParentId, + IN HANDLE ProcessId, + IN BOOLEAN Create +) +{ + PIMAGE_PID_ENTRY p, prev, tmp; + ULONG RequiredPid; + BOOLEAN FoundNewProcess = FALSE; + KIRQL irql; + + + LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("%d CreateProccessNotifyProc(%d, %d, %d)\n", CURRENT_PROCESS_PID, ParentId, ProcessId, Create)); + + + /* if the entry is being removed look for ProcessId, otherwise look for 0 */ + RequiredPid = Create == FALSE ? (ULONG) ProcessId : 0; + + + /* + * find an entry with pid=ProcessId + */ + + KeAcquireSpinLock(&gImagePidHtblSpinLock, &irql); + + prev = &gImagePidHtbl[(ULONG) RequiredPid % IMAGE_PID_HASHTABLE_SIZE]; + p = prev->next; + + while (p) + { + if (p->ProcessId != (ULONG) RequiredPid) + { + prev = p; + p = p->next; + continue; + } + + if (p->ParentId != 0 && p->ParentId != (ULONG) ParentId) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d CreateProccessNotifyProc(): ProcessId %d collision. %d vs %d\n", CURRENT_PROCESS_PID, p->ProcessId, p->ParentId, ParentId)); + + prev = p; + p = p->next; + continue; + } + + + /* found the necessary entry */ + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d CreateProccessNotifyProc(): Found (%s) %d %x %S\n", CURRENT_PROCESS_PID, Create == FALSE ? "delete" : "fix", p->ProcessId, p->next, p->ImageName)); + + tmp = p; + + /* unlink the found entry */ + prev->next = p->next; + + if (Create == FALSE) + { + /* if the process is being deleted then free the entry */ + PolicyDelete(&tmp->SecPolicy); + ExFreePoolWithTag(tmp, _POOL_TAG); + } + else + { + /* + * if the process is being executed and we found an entry with PID of 0 then + * replace 0 with the real pid + */ + tmp->ProcessId = (ULONG) ProcessId; + tmp->next = NULL; + FoundNewProcess = TRUE; + } + + break; + } + + KeReleaseSpinLock(&gImagePidHtblSpinLock, irql); + + + /* + * If necessary, reinsert the new entry that used to have a PID of 0. + * We need to reinsert since the hashtable is indexed by PIDs. + */ + if (FoundNewProcess == TRUE) + { +//XXX at this point no locks are held. if tmp->ProcessId process quits before ProcessInsertImagePidEntry() + // executes, we will get a zombie entry (will never be removed) + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d Replacing 0 with %d for %S\n", CURRENT_PROCESS_PID, tmp->ProcessId, tmp->ImageName)); + ProcessInsertImagePidEntry(tmp->ProcessId, tmp); + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d Done replacing %d\n", CURRENT_PROCESS_PID, tmp->ProcessId, tmp->ImageName)); + } +} + + + +USHORT ProcessNameOffset = 0, ThreadServiceTableOffset = 0; + + +/* + * FindProcessNameOffset() + * + * Description: + * Identifies process name offset in the EPROCESS structure. + * + * The name offset is identified by searching for "System" string in the System EPROCESS structure + * (this function is called when the driver is loaded and thus runs in the "System" context). + * + * ThreadServiceTableOffset is the offset of the pointer to a service descriptor table (syscall table) + * in the Thread Environment Block (TEB). Used to determine whether a thread is GUI based or not. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +void +FindProcessNameOffset() +{ + PEPROCESS SystemProcess = PsGetCurrentProcess(); // current "System" process + PETHREAD SystemThread = PsGetCurrentThread(); // current "System" thread + USHORT i; + + + /* Search for "System" process name in the current system process Process Environment Block */ + + for (i = 0; i < 1024; i++) // 372 on Windows XP SP1 + { + if (_strnicmp((PCHAR) SystemProcess + i, "System", 6) == 0) + { + ProcessNameOffset = i; + break; + } + } + + + /* Search for KeServiceDescriptorTable address in the current system thread's Thread Environment Block */ + + for (i = 0; i < 500; i++) // 292 on Win2k3 + { + if (* (PULONG) ((PCHAR) SystemThread + i) == (ULONG) &KeServiceDescriptorTable[0]) + { + ThreadServiceTableOffset = i; + break; + } + } +} + + + +/* + * GetCurrentProcessName() + * + * Description: + * Returns the current process pathname. + * + * Parameters: + * None. + * + * Returns: + * Pointer to the current process pathname ("Unknown" if not found). + */ + +PWCHAR +GetCurrentProcessName() +{ + PIMAGE_PID_ENTRY p; + PWCHAR name = NULL; + + + p = FindImagePidEntry(CURRENT_PROCESS_PID, 0); + + if (p != NULL) + /* + * NOTE: we are returning a pointer to a member of a structure which is not locked at this point! + * Should be ok though since this function is only called in the context of a current process which + * cannot disappear from underneath us. + */ + name = p->ImageName; + else + name = L"(Unknown)"; + + + return name; +} + + + +/* + * FindExistingProcesses() + * + * Description: + * Enumerates all the existing processes using ZwQuerySystemInformation(SystemProcessesAndThreadsInformation) + * and creates IMAGE_PID_ENTRY for all of them. Called once at startup. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +VOID +EnumerateExistingProcesses() +{ + ULONG size = 65535, i; + PUCHAR SystemInfo; + PSYSTEM_PROCESSES pSystemProcess; + NTSTATUS status; + + + /* + * The format of the data returned to the SystemInformation buffer is a sequence of + * SYSTEM_PROCESSES structures, chained together via the NextEntryDelta member. + * The Threads member of each SYSTEM_PROCESSES structure is an array of ThreadCount + * SYSTEM_THREADS structures.The end of the process chain is marked by a NextEntryDelta + * value of zero. + */ + + /* first, find out the total amount of SystemProcessesAndThreadsInformation to be returned */ +/* + status = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, &i, 0, &size); + if (size == 0 || size > 1024*1024) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("FindExistingProcesses: ZwQuerySystemInformation failed. status=%x size=%d\n", status, size)); + return; + } +*/ + + /* second, allocate the required amount of memory */ + + SystemInfo = ExAllocatePoolWithTag(PagedPool, size, _POOL_TAG); + if (SystemInfo == NULL) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("FindExistingProcesses: out of memory (requested %d bytes)\n", size)); + return; + } + + /* third, request the SystemProcessesAndThreadsInformation */ + + ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, SystemInfo, size, &i); + + + pSystemProcess = (PSYSTEM_PROCESSES) SystemInfo; + + + i = 0; + + while (pSystemProcess->NextEntryDelta != 0) + { + if (pSystemProcess->ProcessName.Length != 0) + { + /* create a new process entry and load the associated security policy (if any) */ + + CreateAndLoadNewProcessEntry(pSystemProcess->ProcessId, &pSystemProcess->ProcessName, FALSE); + + ++i; + } + + pSystemProcess = (PSYSTEM_PROCESSES) ( ((PUCHAR) pSystemProcess) + pSystemProcess->NextEntryDelta); + } + + ExFreePoolWithTag(SystemInfo, _POOL_TAG); + + + /* if no processes exist, the computer must be booting up */ + if (i == 0) + { + UNICODE_STRING System; + + + BootingUp = TRUE; + + /* + * when booting up no processes are listed as existing even though "System" and "Idle" + * have already been initialized. Initialize "System" process entry since it will + * never be created otherwise. + */ + + RtlInitUnicodeString(&System, L"System"); + + CreateAndLoadNewProcessEntry(SystemProcessId, &System, FALSE); + } +} + + + +/* + * InitProcessNameEntries() + * + * Description: + * Initializes process id to process name related data. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitProcessNameEntries() +{ + memset(gImagePidHtbl, 0, sizeof(gImagePidHtbl)); + + KeInitializeSpinLock(&gImagePidHtblSpinLock); + + SystemProcessId = (ULONG) PsGetCurrentProcessId(); + + FindProcessNameOffset(); + + + /* XXX investigate +A driver's process-notify routine is also called with Create set to FALSE, usually when the last thread within a process has terminated and the process address space is about to be deleted. In very rare circumstances, for processes in which no thread was ever created, a driver's process-notify routine is called only at the destruction of the process. */ + if (! NT_SUCCESS( PsSetCreateProcessNotifyRoutine(CreateProcessNotifyProc, FALSE) )) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessNameEntries: PsSetCreateProcessNotifyRoutine() failed\n")); + return FALSE; + } + + + return TRUE; +} + + + +/* + * RemoveProcessNameEntries() + * + * Description: + * Removes the global hash table process entries and PsSetCreateProcessNotifyRoutine() callback. + * + * NOTE: Called once during driver unload (DriverUnload()). + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +VOID +RemoveProcessNameEntries() +{ + int i; + PIMAGE_PID_ENTRY p, tmp; + KIRQL irql; + + +#if HOOK_PROCESS + + if (! NT_SUCCESS( PsSetCreateProcessNotifyRoutine(CreateProcessNotifyProc, TRUE) )) + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("RemoveProcessEntries: PsSetCreateProcessNotifyRoutine remove failed\n")); + +#endif + + + KeAcquireSpinLock(&gImagePidHtblSpinLock, &irql); + + for (i = 0; i < IMAGE_PID_HASHTABLE_SIZE; i++) + { + p = gImagePidHtbl[i].next; + + while (p) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("RemoveProcessEntries: Removing %d %x %S\n", p->ProcessId, p->next, p->ImageName)); + + tmp = p; + p = p->next; + + if (tmp->WaitingForUserRequestId != 0) + { + LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("RemoveProcessEntries: Process (pid=%d) is still waiting for a user request id %d!\n", tmp->ProcessId, tmp->WaitingForUserRequestId)); + } + + PolicyDelete(&tmp->SecPolicy); + ExFreePoolWithTag(tmp, _POOL_TAG); + } + } + + KeReleaseSpinLock(&gImagePidHtblSpinLock, irql); +} diff --git a/procname.h b/procname.h new file mode 100644 index 0000000..01145a9 --- /dev/null +++ b/procname.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * procname.h + * + * Abstract: + * + * This module defines various types used by process id to process name conversion routines. + * + * Author: + * + * Eugene Tsyrklevich 23-Feb-2004 + * + * Revision History: + * + * 07-Apr-2004 ET - Copied from process.h + */ + +#ifndef __PROCNAME_H__ +#define __PROCNAME_H__ + + +#include "userland.h" + + +typedef struct _IMAGE_PID_ENTRY +{ + struct _IMAGE_PID_ENTRY *next; + ULONG ProcessId; + ULONG ParentId; + BOOLEAN FirstThread; // Was more than one thread already created? + // (some actions need to take place only in the main thread) + UCHAR WaitingForUserRequestId; // contains the sequence id of the reply we are waiting for + KEVENT UserlandRequestDoneEvent; + PUSERLAND_REPLY_HEADER UserlandReply; + SECURITY_POLICY SecPolicy; + WCHAR ImageName[1]; + +} IMAGE_PID_ENTRY, *PIMAGE_PID_ENTRY; + + +/* + * 1. The following number should be prime. + * 2. It should also be slightly larger than the "average" number of processes of any given machine to + * minimize the number of hash table collisions (we want O(1) access) and at the same time not + * eating up too much memory (gImagePidHtbl[]). + */ +#define IMAGE_PID_HASHTABLE_SIZE 67 + +extern IMAGE_PID_ENTRY gImagePidHtbl[IMAGE_PID_HASHTABLE_SIZE]; + +extern USHORT ProcessNameOffset, ThreadServiceTableOffset; +extern BOOLEAN BootingUp; + + +BOOLEAN InitProcessNameEntries(); +VOID RemoveProcessNameEntries(); +PIMAGE_PID_ENTRY FindImagePidEntry(ULONG ProcessId, ULONG ParentId); +BOOLEAN ProcessInsertImagePidEntry(ULONG ProcessId, PIMAGE_PID_ENTRY NewProcess); +PIMAGE_PID_ENTRY CreateNewProcessEntry(ULONG ProcessId, ULONG ParentId, PUNICODE_STRING ProcessName, BOOLEAN NewProcess); +//PIMAGE_PID_ENTRY CreateAndLoadNewProcessEntry(ULONG ProcessId, PUNICODE_STRING ProcessName, BOOLEAN NewProcess); +VOID EnumerateExistingProcesses(); +PWCHAR GetCurrentProcessName(); + + +#endif /* __PROCNAME_H__ */ \ No newline at end of file diff --git a/registry.c b/registry.c new file mode 100644 index 0000000..c91b7da --- /dev/null +++ b/registry.c @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * registry.c + * + * Abstract: + * + * This module defines various types used by registry hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 20-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "registry.h" +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" +#include "misc.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitRegistryHooks) +#endif + + +fpZwCreateKey OriginalNtCreateKey = NULL; +fpZwOpenKey OriginalNtOpenKey = NULL; + +fpZwDeleteKey OriginalNtDeleteKey = NULL; + +fpZwSetValueKey OriginalNtSetValueKey = NULL; +fpZwQueryValueKey OriginalNtQueryValueKey = NULL; + + + +/* + * HookedNtCreateKey() + * + * Description: + * This function mediates the NtCreateKey() system service and checks the + * provided registry key against the global and current process security policies. + * + * NOTE: ZwCreateKey creates or opens a registry key object. [NAR] + * + * Parameters: + * Those of NtCreateKey(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateKey(). + */ + +NTSTATUS +NTAPI +HookedNtCreateKey +( + OUT PHANDLE KeyHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG TitleIndex, + IN PUNICODE_STRING Class OPTIONAL, + IN ULONG CreateOptions, + OUT PULONG Disposition OPTIONAL +) +{ + PCHAR FunctionName = "HookedNtCreateKey"; + + + HOOK_ROUTINE_START(REGISTRY); + + + ASSERT(OriginalNtOpenKey); + + rc = OriginalNtCreateKey(KeyHandle, DesiredAccess, ObjectAttributes, TitleIndex, + Class, CreateOptions, Disposition); + + + HOOK_ROUTINE_FINISH(REGISTRY); +} + + + +/* + * HookedNtOpenKey() + * + * Description: + * This function mediates the NtOpenKey() system service and checks the + * provided registry key against the global and current process security policies. + * + * NOTE: ZwOpenKey opens a registry key object. [NAR] + * + * Parameters: + * Those of NtOpenKey(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtOpenKey(). + */ + +NTSTATUS +NTAPI +HookedNtOpenKey +( + OUT PHANDLE KeyHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtOpenKey"; + + + HOOK_ROUTINE_START(REGISTRY); + + + ASSERT(OriginalNtOpenKey); + + rc = OriginalNtOpenKey(KeyHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(REGISTRY); +} + + + +/* + * HookedNtDeleteKey() + * + * Description: + * This function mediates the NtDeleteKey() system service and checks the + * provided registry key against the global and current process security policies. + * + * NOTE: ZwDeleteKey deletes a key in the registry. [NAR] + * + * Parameters: + * Those of NtDeleteKey(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtDeleteKey(). + */ + +NTSTATUS +NTAPI +HookedNtDeleteKey +( + IN HANDLE KeyHandle +) +{ + PCHAR FunctionName = "HookedNtDeleteKey"; + CHAR REGISTRYNAME[MAX_PATH]; + WCHAR REGISTRYNAMEW[MAX_PATH]; + PWSTR KeyName = NULL; + + + HOOK_ROUTINE_ENTER(); + + + if ((KeyName = GetNameFromHandle(KeyHandle, REGISTRYNAMEW, sizeof(REGISTRYNAMEW))) != NULL) + { + sprintf(REGISTRYNAME, "%S", KeyName); + + LOG(LOG_SS_REGISTRY, LOG_PRIORITY_VERBOSE, ("%d %s: %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, REGISTRYNAME)); + + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(REGISTRY, OP_DELETE); + } + } + + + ASSERT(OriginalNtDeleteKey); + + rc = OriginalNtDeleteKey(KeyHandle); + + + if (KeyName) + { + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(REGISTRY, REGISTRYNAME, OP_DELETE); + } + else + { + HOOK_ROUTINE_EXIT(rc); + } +} + + + +/* + * HookedNtSetValueKey() + * + * Description: + * This function mediates the NtSetValueKey() system service and checks the + * provided registry key against the global and current process security policies. + * + * NOTE: ZwSetValueKey updates or adds a value to a key. [NAR] + * + * Parameters: + * Those of NtSetValueKey(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtSetValueKey(). + */ + +NTSTATUS +NTAPI +HookedNtSetValueKey +( + IN HANDLE KeyHandle, + IN PUNICODE_STRING ValueName, + IN ULONG TitleIndex, + IN ULONG Type, + IN PVOID Data, + IN ULONG DataSize +) +{ + CHAR REGISTRYNAME[MAX_PATH]; + WCHAR REGISTRYNAMEW[MAX_PATH]; + PCHAR FunctionName = "HookedNtSetValueKey"; + UNICODE_STRING usValueName; + PWSTR KeyName = NULL; + + + HOOK_ROUTINE_ENTER(); + + + if (VerifyUnicodeString(ValueName, &usValueName) == TRUE) + { + if ((KeyName = GetNameFromHandle(KeyHandle, REGISTRYNAMEW, sizeof(REGISTRYNAMEW))) != NULL) + { + _snprintf(REGISTRYNAME, MAX_PATH, "%S\\%S", KeyName, ValueName->Buffer); + REGISTRYNAME[MAX_PATH - 1] = 0; + + LOG(LOG_SS_REGISTRY, LOG_PRIORITY_VERBOSE, ("%d %s: %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, REGISTRYNAME)); + + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(REGISTRY, OP_WRITE); + } + } + } + + + ASSERT(OriginalNtSetValueKey); + + rc = OriginalNtSetValueKey(KeyHandle, ValueName, TitleIndex, Type, Data, DataSize); + + + if (KeyName) + { + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(REGISTRY, REGISTRYNAME, OP_WRITE); + } + else + { + HOOK_ROUTINE_EXIT(rc); + } +} + + + +/* + * HookedNtQueryValueKey() + * + * Description: + * This function mediates the NtQueryValueKey() system service and checks the + * provided registry key against the global and current process security policies. + * + * NOTE: ZwQueryValueKey retrieves information about a key value. [NAR] + * + * Parameters: + * Those of NtQueryValueKey(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtQueryValueKey(). + */ + +NTSTATUS +NTAPI +HookedNtQueryValueKey +( + IN HANDLE KeyHandle, + IN PUNICODE_STRING ValueName, + IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + OUT PVOID KeyValueInformation, + IN ULONG KeyValueInformationLength, + OUT PULONG ResultLength +) +{ + CHAR REGISTRYNAME[MAX_PATH]; + WCHAR REGISTRYNAMEW[MAX_PATH]; + PCHAR FunctionName = "HookedNtQueryValueKey"; + UNICODE_STRING usValueName; + PWSTR KeyName = NULL; + + + HOOK_ROUTINE_ENTER(); + + + if (VerifyUnicodeString(ValueName, &usValueName) == TRUE) + { + if ((KeyName = GetNameFromHandle(KeyHandle, REGISTRYNAMEW, sizeof(REGISTRYNAMEW))) != NULL) + { + _snprintf(REGISTRYNAME, MAX_PATH, "%S\\%S", KeyName, ValueName->Buffer); + REGISTRYNAME[MAX_PATH - 1] = 0; + + LOG(LOG_SS_REGISTRY, LOG_PRIORITY_VERBOSE, ("%d %s: %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, REGISTRYNAME)); + + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(REGISTRY, OP_READ); + } + } + } + + + ASSERT(OriginalNtQueryValueKey); + + rc = OriginalNtQueryValueKey(KeyHandle, ValueName, KeyValueInformationClass, KeyValueInformation, + KeyValueInformationLength, ResultLength); + + + if (KeyName) + { + HOOK_ROUTINE_FINISH_OBJECTNAME_OPTYPE(REGISTRY, REGISTRYNAME, OP_READ); + } + else + { + HOOK_ROUTINE_EXIT(rc); + } +} + + + +/* + * InitRegistryHooks() + * + * Description: + * Initializes all the mediated registry operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitRegistryHooks() +{ + if ( (OriginalNtCreateKey = (fpZwCreateKey) ZwCalls[ZW_CREATE_KEY_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_REGISTRY, LOG_PRIORITY_DEBUG, ("InitRegistryHooks: OriginalNtCreateKey is NULL\n")); + return FALSE; + } + + if ( (OriginalNtOpenKey = (fpZwOpenKey) ZwCalls[ZW_OPEN_KEY_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_REGISTRY, LOG_PRIORITY_DEBUG, ("InitRegistryHooks: OriginalNtOpenKey is NULL\n")); + return FALSE; + } + + if ( (OriginalNtDeleteKey = (fpZwDeleteKey) ZwCalls[ZW_DELETE_KEY_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_REGISTRY, LOG_PRIORITY_DEBUG, ("InitRegistryHooks: OriginalNtDeleteKey is NULL\n")); + return FALSE; + } + +// XXX ZwDeleteValueKey +/* + if ( (OriginalNtSetValueKey = (fpZwSetValueKey) ZwCalls[ZW_SET_VALUE_KEY_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_REGISTRY, LOG_PRIORITY_DEBUG, ("InitRegistryHooks: OriginalNtSetValueKey is NULL\n")); + return FALSE; + } + + if ( (OriginalNtQueryValueKey = (fpZwQueryValueKey) ZwCalls[ZW_QUERY_VALUE_KEY_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_REGISTRY, LOG_PRIORITY_DEBUG, ("InitRegistryHooks: OriginalNtQueryValueKey is NULL\n")); + return FALSE; + } +*/ + return TRUE; +} diff --git a/registry.h b/registry.h new file mode 100644 index 0000000..d4f5756 --- /dev/null +++ b/registry.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * registry.h + * + * Abstract: + * + * This module defines various types used by registry hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 20-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __REGISTRY_H__ +#define __REGISTRY_H__ + + +/* + * ZwCreateKey creates or opens a registry key object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateKey) ( + OUT PHANDLE KeyHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG TitleIndex, + IN PUNICODE_STRING Class OPTIONAL, + IN ULONG CreateOptions, + OUT PULONG Disposition OPTIONAL + ); + +NTSTATUS +NTAPI +HookedNtCreateKey( + OUT PHANDLE KeyHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG TitleIndex, + IN PUNICODE_STRING Class OPTIONAL, + IN ULONG CreateOptions, + OUT PULONG Disposition OPTIONAL + ); + + +/* + * ZwOpenKey opens a registry key object. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenKey) ( + OUT PHANDLE KeyHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS +NTAPI +HookedNtOpenKey( + OUT PHANDLE KeyHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +/* + * ZwSetValueKey updates or adds a value to a key. [NAR] + */ + +typedef NTSTATUS (*fpZwSetValueKey) ( + IN HANDLE KeyHandle, + IN PUNICODE_STRING ValueName, + IN ULONG TitleIndex, + IN ULONG Type, + IN PVOID Data, + IN ULONG DataSize + ); + +NTSTATUS +NTAPI +HookedNtSetValueKey( + IN HANDLE KeyHandle, + IN PUNICODE_STRING ValueName, + IN ULONG TitleIndex, + IN ULONG Type, + IN PVOID Data, + IN ULONG DataSize + ); + + +/* + * ZwQueryValueKey retrieves information about a key value. [NAR] + */ + +typedef NTSTATUS (*fpZwQueryValueKey) ( + IN HANDLE KeyHandle, + IN PUNICODE_STRING ValueName, + IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + OUT PVOID KeyValueInformation, + IN ULONG KeyValueInformationLength, + OUT PULONG ResultLength + ); + +NTSTATUS +NTAPI +HookedNtQueryValueKey( + IN HANDLE KeyHandle, + IN PUNICODE_STRING ValueName, + IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + OUT PVOID KeyValueInformation, + IN ULONG KeyValueInformationLength, + OUT PULONG ResultLength + ); + + +/* + * ZwDeleteKey deletes a key in the registry. [NAR] + */ + +typedef NTSTATUS (*fpZwDeleteKey) ( + IN HANDLE KeyHandle + ); + +NTSTATUS +NTAPI +HookedNtDeleteKey( + IN HANDLE KeyHandle + ); + + +BOOLEAN InitRegistryHooks(); + + +#endif /* __REGISTRY_H__ */ diff --git a/resource.h b/resource.h new file mode 100644 index 0000000..c2e26c9 --- /dev/null +++ b/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by eugene.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/section.c b/section.c new file mode 100644 index 0000000..ba21b3c --- /dev/null +++ b/section.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * section.c + * + * Abstract: + * + * This module defines various routines used for hooking section objects related routines. + * Section objects are objects that can be mapped into the virtual address space of a process. + * The Win32 API refers to section objects as file-mapping objects. + * + * Hooked routines protect "\Device\PhysicalMemory" device from being accessed. + * + * Author: + * + * Eugene Tsyrklevich 29-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "section.h" +#include "hookproc.h" +#include "pathproc.h" +#include "process.h" +#include "accessmask.h" +#include "procname.h" +#include "learn.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitSectionHooks) +#endif + + +fpZwCreateSection OriginalNtCreateSection = NULL; +fpZwOpenSection OriginalNtOpenSection = NULL; +fpZwMapViewOfSection OriginalNtMapViewOfSection = NULL; + + +//XXX make sure people cannot create symlinks to physicalmemory or we at least resolve all of them! +// http://www.blackhat.com/presentations/bh-usa-03/bh-us-03-rutkowski/bh-us-03-rutkowski-r2.pdf + + +/* + * HookedNtCreateSection() + * + * Description: + * This function mediates the NtCreateSection() system service and checks the + * provided section name against the global and current process security policies. + * + * NOTE: ZwCreateSection creates a section object. [NAR] + * + * Parameters: + * Those of NtCreateSection(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateSection(). + */ + +NTSTATUS +NTAPI +HookedNtCreateSection +( + OUT PHANDLE SectionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PLARGE_INTEGER SectionSize OPTIONAL, + IN ULONG Protect, + IN ULONG Attributes, + IN HANDLE FileHandle +) +{ + PCHAR FunctionName = "HookedNtCreateSection"; + + + HOOK_ROUTINE_START(SECTION); + + + ASSERT(OriginalNtCreateSection); + + rc = OriginalNtCreateSection(SectionHandle, DesiredAccess, ObjectAttributes, SectionSize, + Protect, Attributes, FileHandle); + + +// HOOK_ROUTINE_FINISH(SECTION); + if (LearningMode == TRUE) + { + if (GetPathFromOA(ObjectAttributes, SECTIONNAME, MAX_PATH, DO_NOT_RESOLVE_LINKS)) + { + /* + * Special Case. + * \KnownDlls\* requests are processed as DLL rules. + * + * In addition, they are processed even if NtCreateSection() failed because not + * all the existing DLLs are "known". + */ + + if (_strnicmp(SECTIONNAME, "\\KnownDlls\\", 11) == 0) + { + AddRule(RULE_DLL, SECTIONNAME, Get_SECTION_OperationType(DesiredAccess)); + } + else if (NT_SUCCESS(rc)) + { + AddRule(RULE_SECTION, SECTIONNAME, Get_SECTION_OperationType(DesiredAccess)); + } + } + } + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * HookedNtOpenSection() + * + * Description: + * This function mediates the NtOpenSection() system service and checks the + * provided section name against the global and current process security policies. + * + * NOTE: ZwOpenSection opens a section object. [NAR] + * + * Parameters: + * Those of NtOpenSection(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtOpenSection(). + */ + +NTSTATUS +NTAPI +HookedNtOpenSection +( + OUT PHANDLE SectionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtOpenSection"; + + + HOOK_ROUTINE_START(SECTION); + + + ASSERT(OriginalNtOpenSection); + + rc = OriginalNtOpenSection(SectionHandle, DesiredAccess, ObjectAttributes); + + +// HOOK_ROUTINE_FINISH(SECTION); + if (LearningMode == TRUE) + { + if (GetPathFromOA(ObjectAttributes, SECTIONNAME, MAX_PATH, DO_NOT_RESOLVE_LINKS)) + { + /* + * Special Case. + * \KnownDlls\* requests are processed as DLL rules. + * + * In addition, they are processed even if NtOpenSection() failed because not + * all the existing DLLs are "known". + */ + + if (_strnicmp(SECTIONNAME, "\\KnownDlls\\", 11) == 0) + { + AddRule(RULE_DLL, SECTIONNAME, Get_SECTION_OperationType(DesiredAccess)); + } + else if (NT_SUCCESS(rc)) + { + AddRule(RULE_SECTION, SECTIONNAME, Get_SECTION_OperationType(DesiredAccess)); + } + } + } + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * HookedNtMapViewOfSection() + * + * Description: + * This function mediates the NtMapViewOfSection() system service and checks the + * provided section name against the global and current process security policies. + * + * NOTE: ZwMapViewOfSection maps a view of a section to a range of virtual addresses. [NAR] + * + * Parameters: + * Those of NtMapViewOfSection(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtMapViewOfSection(). + */ + +NTSTATUS +NTAPI +HookedNtMapViewOfSection +( + IN HANDLE SectionHandle, + IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN ULONG ZeroBits, + IN ULONG CommitSize, + IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, + IN OUT PULONG ViewSize, + IN SECTION_INHERIT InheritDisposition, + IN ULONG AllocationType, + IN ULONG Protect +) +{ + CHAR section[512]; + + + HOOK_ROUTINE_ENTER(); + +// LOG(LOG_SS_SECTION, LOG_PRIORITY_DEBUG, ("%d HookedNtMapViewOfSection: %x %x %x %x\n", (ULONG) PsGetCurrentProcessId(), SectionHandle, ProcessHandle, BaseAddress, CommitSize)); +/* + if (GetPathFromOA(ObjectAttributes, section, RESOLVE_LINKS)) + { + LOG(LOG_SS_SECTION, LOG_PRIORITY_DEBUG, ("HookedNtMapViewOfSection: %s\n", section)); +// if (PolicyCheck(&gSecPolicy, key, GetRegistryOperationType(DesiredAccess)) == STATUS_ACCESS_DENIED) + +// HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } +*/ + + ASSERT(OriginalNtMapViewOfSection); + + rc = OriginalNtMapViewOfSection(SectionHandle, ProcessHandle, BaseAddress, ZeroBits, CommitSize, + SectionOffset, ViewSize, InheritDisposition, AllocationType, Protect); + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * InitSectionHooks() + * + * Description: + * Initializes all the mediated section object operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitSectionHooks() +{ + if ( (OriginalNtCreateSection = (fpZwCreateSection) ZwCalls[ZW_CREATE_SECTION_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_SECTION, LOG_PRIORITY_DEBUG, ("InitSectionHooks: OriginalNtCreateSection is NULL\n")); + return FALSE; + } + + if ( (OriginalNtOpenSection = (fpZwOpenSection) ZwCalls[ZW_OPEN_SECTION_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_SECTION, LOG_PRIORITY_DEBUG, ("InitSectionHooks: OriginalNtOpenSection is NULL\n")); + return FALSE; + } +/* + if ((OriginalNtMapViewOfSection = (fpZwMapViewOfSection) ZwCalls[ZW_MAPVIEW_SECTION_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_SECTION, LOG_PRIORITY_DEBUG, ("InitSectionHooks: OriginalNtMapViewOfSection is NULL\n")); + return FALSE; + } +*/ + return TRUE; +} diff --git a/section.h b/section.h new file mode 100644 index 0000000..7e41076 --- /dev/null +++ b/section.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * section.h + * + * Abstract: + * + * This module defines various types used by section hooking related routines. + * + * Author: + * + * Eugene Tsyrklevich 29-Feb-2004 + * + * Revision History: + * + * None. + */ + +#ifndef __MEMORY_H__ +#define __MEMORY_H__ + + + +/* + * "Section objects are objects that can be mapped into the virtual address space of a process. + * The Win32 API refers to section objects as file-mapping objects. + * + * ZwOpenSection opens a section object." [NAR] + */ + +typedef NTSTATUS (*fpZwOpenSection) ( + OUT PHANDLE SectionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +NTSTATUS +NTAPI +HookedNtOpenSection( + OUT PHANDLE SectionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +/* + * ZwCreateSection creates a section object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateSection) ( + OUT PHANDLE SectionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PLARGE_INTEGER SectionSize OPTIONAL, + IN ULONG Protect, + IN ULONG Attributes, + IN HANDLE FileHandle + ); + +NTSTATUS +NTAPI +HookedNtCreateSection( + OUT PHANDLE SectionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PLARGE_INTEGER SectionSize OPTIONAL, + IN ULONG Protect, + IN ULONG Attributes, + IN HANDLE FileHandle + ); + + +/* + * ZwMapViewOfSection maps a view of a section to a range of virtual addresses. [NAR] + */ + +typedef NTSTATUS (*fpZwMapViewOfSection) ( + IN HANDLE SectionHandle, + IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN ULONG ZeroBits, + IN ULONG CommitSize, + IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, + IN OUT PULONG ViewSize, + IN SECTION_INHERIT InheritDisposition, + IN ULONG AllocationType, + IN ULONG Protect + ); + +NTSTATUS +NTAPI +HookedNtMapViewOfSection( + IN HANDLE SectionHandle, + IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN ULONG ZeroBits, + IN ULONG CommitSize, + IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, + IN OUT PULONG ViewSize, + IN SECTION_INHERIT InheritDisposition, + IN ULONG AllocationType, + IN ULONG Protect + ); + + +BOOLEAN InitSectionHooks(); + + +#endif /* __MEMORY_H__ */ diff --git a/semaphore.c b/semaphore.c new file mode 100644 index 0000000..fe12258 --- /dev/null +++ b/semaphore.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * semaphore.c + * + * Abstract: + * + * This module implements various semaphore hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 09-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "semaphore.h" +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitSemaphoreHooks) +#endif + + +fpZwCreateSemaphore OriginalNtCreateSemaphore = NULL; +fpZwOpenSemaphore OriginalNtOpenSemaphore = NULL; + + + +/* + * HookedNtCreateSemaphore() + * + * Description: + * This function mediates the NtCreateSemaphore() system service and checks the + * provided semaphore name against the global and current process security policies. + * + * NOTE: ZwOpenSemaphore opens a semaphore object. [NAR] + * + * Parameters: + * Those of NtCreateSemaphore(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateSemaphore(). + */ + +NTSTATUS +NTAPI +HookedNtCreateSemaphore +( + OUT PHANDLE SemaphoreHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN LONG InitialCount, + IN LONG MaximumCount +) +{ + PCHAR FunctionName = "HookedNtCreateSemaphore"; + + + HOOK_ROUTINE_START(SEMAPHORE); + + + ASSERT(OriginalNtCreateSemaphore); + + rc = OriginalNtCreateSemaphore(SemaphoreHandle, DesiredAccess, ObjectAttributes, InitialCount, MaximumCount); + + + HOOK_ROUTINE_FINISH(SEMAPHORE); +} + + + + +/* + * HookedNtOpenSemaphore() + * + * Description: + * This function mediates the NtOpenSemaphore() system service and checks the + * provided semaphore name against the global and current process security policies. + * + * NOTE: ZwOpenSemaphore opens a semaphore object. [NAR] + * + * Parameters: + * Those of NtOpenSemaphore(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtOpenSemaphore(). + */ + +NTSTATUS +NTAPI +HookedNtOpenSemaphore +( + OUT PHANDLE SemaphoreHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtOpenSemaphore"; + + + HOOK_ROUTINE_START(SEMAPHORE); + + + ASSERT(OriginalNtOpenSemaphore); + + rc = OriginalNtOpenSemaphore(SemaphoreHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(SEMAPHORE); +} + + + +/* + * InitSemaphoreHooks() + * + * Description: + * Initializes all the mediated semaphore operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitSemaphoreHooks() +{ + if ( (OriginalNtCreateSemaphore = (fpZwCreateSemaphore) ZwCalls[ZW_CREATE_SEMAPHORE_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_SEMAPHORE, LOG_PRIORITY_DEBUG, ("InstallSemaphoreHooks: OriginalNtCreateSemaphore is NULL\n")); + return FALSE; + } + + if ( (OriginalNtOpenSemaphore = (fpZwOpenSemaphore) ZwCalls[ZW_OPEN_SEMAPHORE_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_SEMAPHORE, LOG_PRIORITY_DEBUG, ("InstallSemaphoreHooks: OriginalNtOpenSemaphore is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/semaphore.h b/semaphore.h new file mode 100644 index 0000000..856095a --- /dev/null +++ b/semaphore.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * semaphore.h + * + * Abstract: + * + * This module defines various types used by semaphore hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 09-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __SEMAPHORE_H__ +#define __SEMAPHORE_H__ + + + +/* + * ZwCreateSemaphore creates or opens a semaphore object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateSemaphore) ( + OUT PHANDLE SemaphoreHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN LONG InitialCount, + IN LONG MaximumCount + ); + +NTSTATUS HookedNtCreateSemaphore( + OUT PHANDLE SemaphoreHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN LONG InitialCount, + IN LONG MaximumCount + ); + + +/* + * ZwOpenSemaphore opens a semaphore object. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenSemaphore) ( + OUT PHANDLE SemaphoreHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS HookedNtOpenSemaphore( + OUT PHANDLE SemaphoreHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + + +BOOLEAN InitSemaphoreHooks(); + + +#endif /* __SEMAPHORE_H__ */ diff --git a/symlink.c b/symlink.c new file mode 100644 index 0000000..237ac97 --- /dev/null +++ b/symlink.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * symlink.c + * + * Abstract: + * + * This module implements various symbolic link object hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include "symlink.h" +#include "media.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitSymlinkHooks) +#endif + + +fpZwCreateSymbolicLinkObject OriginalNtCreateSymbolicLinkObject = NULL; +fpZwOpenSymbolicLinkObject OriginalNtOpenSymbolicLinkObject = NULL; + + +/* + * HookedNtCreateSymbolicLinkObject() + * + * Description: + * This function mediates the NtCreateSymbolicLinkObject() system service and checks the + * provided symbolic link object name against the global and current process security policies. + * + * NOTE: ZwCreateSymbolicLinkObject creates or opens a symbolic link object. [NAR] + * + * Parameters: + * Those of NtCreateSymbolicLinkObject(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateSymbolicLinkObject(). + */ + +NTSTATUS +NTAPI +HookedNtCreateSymbolicLinkObject +( + OUT PHANDLE SymbolicLinkHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PUNICODE_STRING TargetName +) +{ + PCHAR FunctionName = "HookedNtCreateSymbolicLinkObject"; + CHAR SYMLINKNAME[MAX_PATH]; + + + HOOK_ROUTINE_ENTER(); + + + if (LearningMode == FALSE && GetPathFromOA(ObjectAttributes, SYMLINKNAME, MAX_PATH, RESOLVE_LINKS)) + { + /* TargetName is not verified to be valid but it's ok as we don't use it for anything but printing (in debugging mode only) */ + if (TargetName) + LOG(LOG_SS_SYMLINK, LOG_PRIORITY_VERBOSE, ("%d HookedNtCreateSymbolicLinkObject: %s -> %S\n", (ULONG) PsGetCurrentProcessId(), SYMLINKNAME, TargetName->Buffer)); + else + LOG(LOG_SS_SYMLINK, LOG_PRIORITY_VERBOSE, ("%d HookedNtCreateSymbolicLinkObject: %s ->.\n", (ULONG) PsGetCurrentProcessId(), SYMLINKNAME)); + + + POLICY_CHECK_OPTYPE_NAME(SYMLINK, Get_SYMLINK_OperationType(DesiredAccess)); + } + + + ASSERT(OriginalNtCreateSymbolicLinkObject); + + rc = OriginalNtCreateSymbolicLinkObject(SymbolicLinkHandle, DesiredAccess, ObjectAttributes, TargetName); + + +#if HOOK_MEDIA + /* removable media hook */ + if (LearningMode == FALSE && NT_SUCCESS(rc) && KeGetPreviousMode() == KernelMode) + { + MonitorDriveLinks(SYMLINKNAME); + } +#endif + + + HOOK_ROUTINE_FINISH(SYMLINK); +} + + + +/* + * HookedNtOpenSymbolicLinkObject() + * + * Description: + * This function mediates the NtOpenSymbolicLinkObject() system service and checks the + * provided symbolic link object name against the global and current process security policies. + * + * NOTE: ZwOpenSymbolicLinkObject opens a symbolic link object. [NAR] + * + * Parameters: + * Those of NtOpenSymbolicLinkObject(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtOpenSymbolicLinkObject(). + */ + +NTSTATUS +NTAPI +HookedNtOpenSymbolicLinkObject +( + OUT PHANDLE SymbolicLinkHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtOpenSymbolicLinkObject"; + CHAR SYMLINKNAME[MAX_PATH]; + + + HOOK_ROUTINE_ENTER(); + + + /* Cannot use RESOLVE_LINKS! (to avoid infinite recursion) */ + if (LearningMode == FALSE && GetPathFromOA(ObjectAttributes, SYMLINKNAME, MAX_PATH, DO_NOT_RESOLVE_LINKS)) + { + POLICY_CHECK_OPTYPE_NAME(SYMLINK, Get_SYMLINK_OperationType(DesiredAccess)); + } + + + ASSERT(OriginalNtOpenSymbolicLinkObject); + + rc = OriginalNtOpenSymbolicLinkObject(SymbolicLinkHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(SYMLINK); +} + + + +/* + * InitSymlinkHooks() + * + * Description: + * Initializes all the mediated symbolic link object operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitSymlinkHooks() +{ + if ( (OriginalNtCreateSymbolicLinkObject = (fpZwCreateSymbolicLinkObject) ZwCalls[ZW_CREATE_SYMLINK_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_SYMLINK, LOG_PRIORITY_DEBUG, ("InitSymlinkHooks: OriginalNtCreateSymbolicLinkObject is NULL\n")); + return FALSE; + } +/* + disabled due to performance issues - this function is called by every system call (from ResolveFilename) + + if ( (OriginalNtOpenSymbolicLinkObject = (fpZwOpenSymbolicLinkObject) ZwCalls[ZW_OPEN_SYMLINK_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_SYMLINK, LOG_PRIORITY_DEBUG, ("InitSymlinkHooks: OriginalNtOpenSymbolicLinkObject is NULL\n")); + return FALSE; + } +*/ + return TRUE; +} diff --git a/symlink.h b/symlink.h new file mode 100644 index 0000000..2a6abf9 --- /dev/null +++ b/symlink.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * symlink.h + * + * Abstract: + * + * This module defines various types used by symbolic link object hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __SYMLINK_H__ +#define __SYMLINK_H__ + + +#include +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" +#include "log.h" + + +/* + * ZwCreateSymbolicLinkObject creates or opens a symbolic link object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateSymbolicLinkObject) ( + OUT PHANDLE SymbolicLinkHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PUNICODE_STRING TargetName + ); + +NTSTATUS +NTAPI +HookedNtCreateSymbolicLinkObject( + OUT PHANDLE SymbolicLinkHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PUNICODE_STRING TargetName + ); + + +/* + * ZwOpenSymbolicLinkObject opens a symbolic link object. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenSymbolicLinkObject) ( + OUT PHANDLE SymbolicLinkHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS +NTAPI +HookedNtOpenSymbolicLinkObject( + OUT PHANDLE SymbolicLinkHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +BOOLEAN InitSymlinkHooks(); + + +#endif /* __SYMLINK_H__ */ diff --git a/sysinfo.c b/sysinfo.c new file mode 100644 index 0000000..5e08179 --- /dev/null +++ b/sysinfo.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * sysinfo.c + * + * Abstract: + * + * This module defines various routines used for hooking ZwSetSystemInformation() routine. + * ZwSetSystemInformation's SystemLoadAndCallImage and SystemLoadImage parameters can be used + * to load code into kernel address space. + * + * Author: + * + * Eugene Tsyrklevich 01-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "sysinfo.h" +#include "hookproc.h" +#include "procname.h" +#include "learn.h" +#include "time.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitSysInfoHooks) +#endif + + +fpZwSetSystemInformation OriginalNtSetSystemInformation = NULL; + + +/* + * HookedNtSetSystemInformation() + * + * Description: + * This function mediates the NtSetSystemInformation() system service and disallows access to + * Information Classes 26 (SystemLoadImage) and 38 (SystemLoadAndCallImage) which allow + * applications to load code into kernel memory. + * + * NOTE: ZwSetSystemInformation sets information that affects the operation of the system. [NAR] + * + * Parameters: + * Those of NtSetSystemInformation(). + * + * Returns: + * STATUS_ACCESS_DENIED if Information Class 26 or 38 is used. + * Otherwise, NTSTATUS returned by NtSetSystemInformation(). + */ + +NTSTATUS +NTAPI +HookedNtSetSystemInformation +( + IN SYSTEM_INFORMATION_CLASS SystemInformationClass, + IN OUT PVOID SystemInformation, + IN ULONG SystemInformationLength +) +{ + PCHAR FunctionName = "HookedNtSetSystemInformation"; + + + HOOK_ROUTINE_ENTER(); + + + if (SystemInformationClass == SystemLoadImage || + SystemInformationClass == SystemLoadAndCallImage) + { + UNICODE_STRING usImageName; + ANSI_STRING asImageName; + CHAR DriverNameUnresolved[MAX_PATH]; + + + if (!VerifyUnicodeString(SystemInformation, &usImageName)) + { + LOG(LOG_SS_PORT, LOG_PRIORITY_DEBUG, ("%s: VerifyUnicodeString failed\n", FunctionName)); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + if (_snprintf(DriverNameUnresolved, MAX_PATH, "%S", usImageName.Buffer) < 0) + { + LOG(LOG_SS_DRIVER, LOG_PRIORITY_DEBUG, ("%s: Driver name '%S' is too long\n", FunctionName, usImageName.Buffer)); + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + LOG(LOG_SS_SYSINFO, LOG_PRIORITY_VERBOSE, ("%d %s: SystemLoad %d %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, SystemInformationClass, DriverNameUnresolved)); + + + /* + * Verify the image name against the security policy + */ + + if (LearningMode == FALSE) + { + CHAR DRIVERNAME[MAX_PATH]; + + + FixupFilename(DriverNameUnresolved, DRIVERNAME, MAX_PATH); + + LOG(LOG_SS_SYSINFO, LOG_PRIORITY_VERBOSE, ("%d %s: SystemLoad %d %s\n", (ULONG) PsGetCurrentProcessId(), FunctionName, SystemInformationClass, DRIVERNAME)); + + POLICY_CHECK_OPTYPE_NAME(DRIVER, OP_LOAD); + } + else + { + AddRule(RULE_DRIVER, DriverNameUnresolved, OP_LOAD); + } + } + else if (SystemInformationClass == SystemUnloadImage) + { + LOG(LOG_SS_SYSINFO, LOG_PRIORITY_VERBOSE, ("%d HookedNtSetSystemInformation: SystemUnloadImage %x\n", (ULONG) PsGetCurrentProcessId(), SystemInformation)); + } + else if (SystemInformationClass == SystemTimeAdjustment) + { + LOG(LOG_SS_SYSINFO, LOG_PRIORITY_VERBOSE, ("%d HookedNtSetSystemInformation: SystemTimeAdjustment\n", (ULONG) PsGetCurrentProcessId())); + + + if (LearningMode == FALSE) + { + PCHAR TIMENAME = NULL; /* allow the use of POLICY_CHECK_OPTYPE_NAME() macro */ + + POLICY_CHECK_OPTYPE_NAME(TIME, OP_TIME_CHANGE); + } + else if (LearningMode == TRUE) + { + AddRule(RULE_TIME, NULL, OP_TIME_CHANGE); + } + } + else if (SystemInformationClass == SystemProcessesAndThreadsInformation) + { + LOG(LOG_SS_SYSINFO, LOG_PRIORITY_DEBUG, ("%d HookedNtSetSystemInformation: SystemProcessesAndThreadsInformation\n", (ULONG) PsGetCurrentProcessId())); + } + else if (SystemInformationClass == SystemModuleInformation) + { + LOG(LOG_SS_SYSINFO, LOG_PRIORITY_DEBUG, ("%d HookedNtSetSystemInformation: SystemModuleInformation\n", (ULONG) PsGetCurrentProcessId())); + } + else if (SystemInformationClass == SystemCreateSession) + { + LOG(LOG_SS_SYSINFO, LOG_PRIORITY_VERBOSE, ("%d HookedNtSetSystemInformation: SystemCreateSession %x %x\n", (ULONG) PsGetCurrentProcessId(), SystemInformation, *(PULONG) SystemInformation)); + } + else if (SystemInformationClass == SystemDeleteSession) + { + LOG(LOG_SS_SYSINFO, LOG_PRIORITY_VERBOSE, ("%d HookedNtSetSystemInformation: SystemDeleteSession %x %x\n", (ULONG) PsGetCurrentProcessId(), SystemInformation, *(PULONG) SystemInformation)); + } + else if (SystemInformationClass == SystemSessionProcessesInformation) + { + LOG(LOG_SS_SYSINFO, LOG_PRIORITY_DEBUG, ("%d HookedNtSetSystemInformation: SystemSessionProcessesInformation\n", (ULONG) PsGetCurrentProcessId())); + } + + + ASSERT(OriginalNtSetSystemInformation); + + rc = OriginalNtSetSystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength); + + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * InitSysInfoHooks() + * + * Description: + * Initializes all the mediated system information operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitSysInfoHooks() +{ + if ((OriginalNtSetSystemInformation = (fpZwSetSystemInformation) ZwCalls[ZW_SET_SYSTEM_INFORMATION_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_SYSINFO, LOG_PRIORITY_DEBUG, ("InitSysInfoHooks: OriginalNtSetSystemInformation is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/sysinfo.h b/sysinfo.h new file mode 100644 index 0000000..60a0a9a --- /dev/null +++ b/sysinfo.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * sysinfo.h + * + * Abstract: + * + * This module defines various types used by ZwSetSystemInformation() hooking routines. + * ZwSetSystemInformation's SystemLoadAndCallImage and SystemLoadImage parameters can be used + * to load code into kernel address space. + * + * Author: + * + * Eugene Tsyrklevich 01-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __SYSINFO_H__ +#define __SYSINFO_H__ + + + +/* + * ZwSetSystemInformation sets information that affects the operation of the system. [NAR] + */ + // # Query Set +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemBasicInformation, // 0 Y N + SystemProcessorInformation, // 1 Y N + SystemPerformanceInformation, // 2 Y N + SystemTimeOfDayInformation, // 3 Y N + SystemNotImplemented1, // 4 Y N // SystemPathInformation + SystemProcessesAndThreadsInformation, // 5 Y N + SystemCallCounts, // 6 Y N + SystemConfigurationInformation, // 7 Y N + SystemProcessorTimes, // 8 Y N + SystemGlobalFlag, // 9 Y Y + SystemNotImplemented2, // 10 Y N // SystemCallTimeInformation + SystemModuleInformation, // 11 Y N + SystemLockInformation, // 12 Y N + SystemNotImplemented3, // 13 Y N // SystemStackTraceInformation + SystemNotImplemented4, // 14 Y N // SystemPagedPoolInformation + SystemNotImplemented5, // 15 Y N // SystemNonPagedPoolInformation + SystemHandleInformation, // 16 Y N + SystemObjectInformation, // 17 Y N + SystemPagefileInformation, // 18 Y N + SystemInstructionEmulationCounts, // 19 Y N + SystemInvalidInfoClass1, // 20 + SystemCacheInformation, // 21 Y Y + SystemPoolTagInformation, // 22 Y N + SystemProcessorStatistics, // 23 Y N + SystemDpcInformation, // 24 Y Y + SystemNotImplemented6, // 25 Y N // SystemFullMemoryInformation + SystemLoadImage, // 26 N Y // SystemLoadGdiDriverInformation + SystemUnloadImage, // 27 N Y + SystemTimeAdjustment, // 28 Y Y + SystemNotImplemented7, // 29 Y N // SystemSummaryMemoryInformation + SystemNotImplemented8, // 30 Y N // SystemNextEventIdInformation + SystemNotImplemented9, // 31 Y N // SystemEventIdsInformation + SystemCrashDumpInformation, // 32 Y N + SystemExceptionInformation, // 33 Y N + SystemCrashDumpStateInformation, // 34 Y Y/N + SystemKernelDebuggerInformation, // 35 Y N + SystemContextSwitchInformation, // 36 Y N + SystemRegistryQuotaInformation, // 37 Y Y + SystemLoadAndCallImage, // 38 N Y // SystemExtendServiceTableInformation + SystemPrioritySeparation, // 39 N Y + SystemNotImplemented10, // 40 Y N // SystemPlugPlayBusInformation + SystemNotImplemented11, // 41 Y N // SystemDockInformation + SystemInvalidInfoClass2, // 42 // SystemPowerInformation + SystemInvalidInfoClass3, // 43 // SystemProcessorSpeedInformation + SystemTimeZoneInformation, // 44 Y N + SystemLookasideInformation, // 45 Y N + SystemSetTimeSlipEvent, // 46 N Y + SystemCreateSession, // 47 N Y + SystemDeleteSession, // 48 N Y + SystemInvalidInfoClass4, // 49 + SystemRangeStartInformation, // 50 Y N + SystemVerifierInformation, // 51 Y Y + SystemAddVerifier, // 52 N Y + SystemSessionProcessesInformation // 53 Y N +} SYSTEM_INFORMATION_CLASS; + + +/* + * Information Class 5 + */ + +typedef enum { + StateInitialized, + StateReady, + StateRunning, + StateStandby, + StateTerminated, + StateWait, + StateTransition, + StateUnknown +} THREAD_STATE; + +typedef struct _SYSTEM_THREADS { + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER CreateTime; + ULONG WaitTime; + PVOID StartAddress; + CLIENT_ID ClientId; + KPRIORITY Priority; + KPRIORITY BasePriority; + ULONG ContextSwitchCount; + THREAD_STATE State; + KWAIT_REASON WaitReason; +} SYSTEM_THREADS, *PSYSTEM_THREADS; + +typedef struct _SYSTEM_PROCESSES { + ULONG NextEntryDelta; + ULONG ThreadCount; + ULONG Reserved1[6]; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ProcessName; + KPRIORITY BasePriority; + ULONG ProcessId; + ULONG InheritedFromProcessId; + ULONG HandleCount; + ULONG Reserved2[2]; + VM_COUNTERS VmCounters; + IO_COUNTERS IoCounters; // Windows 2000 only + SYSTEM_THREADS Threads[1]; +} SYSTEM_PROCESSES, *PSYSTEM_PROCESSES; + + +NTSTATUS +NTAPI +HookedNtSetSystemInformation( + IN SYSTEM_INFORMATION_CLASS SystemInformationClass, + IN OUT PVOID SystemInformation, + IN ULONG SystemInformationLength + ); + + +/* + * ZwQuerySystemInformation queries information about the system. [NAR] + */ + +NTSYSAPI +NTSTATUS +NTAPI +ZwQuerySystemInformation( + IN SYSTEM_INFORMATION_CLASS SystemInformationClass, + IN OUT PVOID SystemInformation, + IN ULONG SystemInformationLength, + OUT PULONG ReturnLength OPTIONAL + ); + + +typedef NTSTATUS (*fpZwSetSystemInformation) +( + IN SYSTEM_INFORMATION_CLASS SystemInformationClass, + IN OUT PVOID SystemInformation, + IN ULONG SystemInformationLength +); + + +/* + * Information Class 38 + * + * "This information class can only be set. Rather than setting any information (in a narrow + * sense of “setting”), it performs the operation of loading a module into the kernel + * address space and calling its entry point." [NAR] + */ + +typedef struct _SYSTEM_LOAD_AND_CALL_IMAGE { + + UNICODE_STRING ModuleName; /* The full path in the native NT format of the module to load. */ + +} SYSTEM_LOAD_AND_CALL_IMAGE, *PSYSTEM_LOAD_AND_CALL_IMAGE; + + + +BOOLEAN InitSysInfoHooks(); + + +#endif /* __SYSINFO_H__ */ diff --git a/time.c b/time.c new file mode 100644 index 0000000..448fe7c --- /dev/null +++ b/time.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * time.c + * + * Abstract: + * + * This module defines various routines used for hooking time routines. + * + * Author: + * + * Eugene Tsyrklevich 10-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "time.h" +#include "hookproc.h" +#include "procname.h" +#include "learn.h" +#include "misc.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitTimeHooks) +#endif + + +fpZwSetSystemTime OriginalNtSetSystemTime = NULL; +fpZwSetTimerResolution OriginalNtSetTimerResolution = NULL; + + + +/* + * HookedNtSetSystemTime() + * + * Description: + * This function mediates the NtSetSystemTime() system service and disallows applications + * to change the system time. + * + * NOTE: ZwSetSystemTime sets the system time. [NAR] + * + * Parameters: + * Those of NtSetSystemTime(). + * + * Returns: + * STATUS_ACCESS_DENIED if time changing is disabled. + * Otherwise, NTSTATUS returned by NtSetSystemTime(). + */ + +NTSTATUS +NTAPI +HookedNtSetSystemTime +( + IN PLARGE_INTEGER NewTime, + OUT PLARGE_INTEGER OldTime OPTIONAL +) +{ + PCHAR FunctionName = "HookedNtSetSystemTime"; + PCHAR TIMENAME = NULL; /* allow the use of POLICY_CHECK_OPTYPE_NAME() macro */ + + + HOOK_ROUTINE_ENTER(); + + + LOG(LOG_SS_TIME, LOG_PRIORITY_DEBUG, ("%d (%S) HookedNtSetSystemTime\n", (ULONG) PsGetCurrentProcessId(), GetCurrentProcessName())); + + + /* NOTE: same code is replicated in sysinfo.c */ + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(TIME, OP_TIME_CHANGE); + } + + + ASSERT(OriginalNtSetSystemTime); + + rc = OriginalNtSetSystemTime(NewTime, OldTime); + + + if (LearningMode == TRUE) + { + AddRule(RULE_TIME, NULL, OP_TIME_CHANGE); + } + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * HookedNtSetTimerResolution() + * + * Description: + * This function mediates the NtSetTimerResolution() system service and disallows applications + * to change the system time. + * + * NOTE: ZwSetTimerResolution sets the resolution of the system timer. [NAR] + * + * Parameters: + * Those of NtSetTimerResolution(). + * + * Returns: + * STATUS_ACCESS_DENIED if time changing is disabled. + * Otherwise, NTSTATUS returned by NtSetTimerResolution(). + */ + +NTSTATUS +NTAPI +HookedNtSetTimerResolution +( + IN ULONG RequestedResolution, + IN BOOLEAN Set, + OUT PULONG ActualResolution +) +{ + PCHAR FunctionName = "HookedNtSetTimerResolution"; + PCHAR TIMENAME = NULL; /* allow the use of POLICY_CHECK_OPTYPE_NAME() macro */ + + + HOOK_ROUTINE_ENTER(); + + + LOG(LOG_SS_TIME, LOG_PRIORITY_DEBUG, ("%d (%S) HookedNtSetTimerResolution\n", (ULONG) PsGetCurrentProcessId(), GetCurrentProcessName())); + + + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(TIME, OP_TIME_CHANGE); + } + + + ASSERT(OriginalNtSetTimerResolution); + + rc = OriginalNtSetTimerResolution(RequestedResolution, Set, ActualResolution); + + + if (LearningMode == TRUE) + { + AddRule(RULE_TIME, NULL, OP_TIME_CHANGE); + } + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * InitTimeHooks() + * + * Description: + * Initializes all the mediated time operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitTimeHooks() +{ + if ((OriginalNtSetSystemTime = (fpZwSetSystemTime) ZwCalls[ZW_SET_SYSTEM_TIME_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_TIME, LOG_PRIORITY_DEBUG, ("InitTimeHooks: OriginalNtSetSystemTime is NULL\n")); + return FALSE; + } + + /* a lot of applications seem to be calling this function thus don't intercept it */ +/* + if ((OriginalNtSetTimerResolution = (fpZwSetTimerResolution) ZwCalls[ZW_SET_TIMER_RESOLUTION_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_TIME, LOG_PRIORITY_DEBUG, ("InitTimeHooks: OriginalNtSetTimerResolution is NULL\n")); + return FALSE; + } +*/ + return TRUE; +} diff --git a/time.h b/time.h new file mode 100644 index 0000000..76ccc36 --- /dev/null +++ b/time.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * time.h + * + * Abstract: + * + * This module defines various types used by time hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 10-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __TIME_H__ +#define __TIME_H__ + + +#include + + +/* + * ZwSetSystemTime sets the system time. [NAR] + */ + +typedef NTSTATUS (*fpZwSetSystemTime)( + IN PLARGE_INTEGER NewTime, + OUT PLARGE_INTEGER OldTime OPTIONAL + ); + +NTSTATUS +NTAPI +HookedNtSetSystemTime( + IN PLARGE_INTEGER NewTime, + OUT PLARGE_INTEGER OldTime OPTIONAL + ); + + +/* + * ZwSetTimerResolution sets the resolution of the system timer. [NAR] + */ + +typedef NTSTATUS (*fpZwSetTimerResolution)( + IN ULONG RequestedResolution, + IN BOOLEAN Set, + OUT PULONG ActualResolution + ); + +NTSTATUS +NTAPI +HookedNtSetTimerResolution( + IN ULONG RequestedResolution, + IN BOOLEAN Set, + OUT PULONG ActualResolution + ); + + +BOOLEAN InitTimeHooks(); + + +#endif /* __TIME_H__ */ diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..3210000 --- /dev/null +++ b/timer.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * timer.c + * + * Abstract: + * + * This module implements various timer hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include "timer.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitTimerHooks) +#endif + + +fpZwCreateTimer OriginalNtCreateTimer = NULL; +fpZwOpenTimer OriginalNtOpenTimer = NULL; + + +/* + * HookedNtCreateTimer() + * + * Description: + * This function mediates the NtCreateTimer() system service and checks the + * provided timer name against the global and current process security policies. + * + * NOTE: ZwCreateTimer creates or opens a timer object. [NAR] + * + * Parameters: + * Those of NtCreateTimer(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtCreateTimer(). + */ + +NTSTATUS +NTAPI +HookedNtCreateTimer +( + OUT PHANDLE TimerHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN TIMER_TYPE TimerType +) +{ + PCHAR FunctionName = "HookedNtCreateTimer"; + + + HOOK_ROUTINE_START(TIMER); + + + ASSERT(OriginalNtCreateTimer); + + rc = OriginalNtCreateTimer(TimerHandle, DesiredAccess, ObjectAttributes, TimerType); + + + HOOK_ROUTINE_FINISH(TIMER); +} + + + +/* + * HookedNtOpenTimer() + * + * Description: + * This function mediates the NtOpenTimer() system service and checks the + * provided timer name against the global and current process security policies. + * + * NOTE: ZwOpenTimer opens a timer object. [NAR] + * + * Parameters: + * Those of NtOpenTimer(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtOpenTimer(). + */ + +NTSTATUS +NTAPI +HookedNtOpenTimer +( + OUT PHANDLE TimerHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + PCHAR FunctionName = "HookedNtOpenTimer"; + + + HOOK_ROUTINE_START(TIMER); + + + ASSERT(OriginalNtOpenTimer); + + rc = OriginalNtOpenTimer(TimerHandle, DesiredAccess, ObjectAttributes); + + + HOOK_ROUTINE_FINISH(TIMER); +} + + + +/* + * InitTimerHooks() + * + * Description: + * Initializes all the mediated timer operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitTimerHooks() +{ + if ( (OriginalNtCreateTimer = (fpZwCreateTimer) ZwCalls[ZW_CREATE_TIMER_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_TIMER, LOG_PRIORITY_DEBUG, ("InitTimerHooks: OriginalNtCreateTimer is NULL\n")); + return FALSE; + } + + if ( (OriginalNtOpenTimer = (fpZwOpenTimer) ZwCalls[ZW_OPEN_TIMER_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_TIMER, LOG_PRIORITY_DEBUG, ("InitTimerHooks: OriginalNtOpenTimer is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/timer.h b/timer.h new file mode 100644 index 0000000..9b0ae85 --- /dev/null +++ b/timer.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * timer.h + * + * Abstract: + * + * This module defines various types used by timer object hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __TIMER_H__ +#define __TIMER_H__ + + +#include +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "accessmask.h" +#include "learn.h" +#include "log.h" + + +/* + * ZwCreateTimer creates or opens a timer object. [NAR] + */ + +typedef NTSTATUS (*fpZwCreateTimer) ( + OUT PHANDLE TimerHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN TIMER_TYPE TimerType + ); + +NTSTATUS +NTAPI +HookedNtCreateTimer( + OUT PHANDLE TimerHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN TIMER_TYPE TimerType + ); + + +/* + * ZwOpenTimer opens a timer object. [NAR] + */ + +typedef NTSTATUS (*fpZwOpenTimer) ( + OUT PHANDLE TimerHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS +NTAPI +HookedNtOpenTimer( + OUT PHANDLE TimerHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ); + + +BOOLEAN InitTimerHooks(); + + +#endif /* __TIMER_H__ */ diff --git a/token.c b/token.c new file mode 100644 index 0000000..2f7e45b --- /dev/null +++ b/token.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * token.c + * + * Abstract: + * + * This module implements various token hooking routines. + * Token objects encapsulate the privileges and access rights of an agent + * (a thread or process). + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#include "token.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitTokenHooks) +#endif + + +fpZwAdjustPrivilegesToken OriginalNtAdjustPrivilegesToken = NULL; +fpZwSetInformationToken OriginalNtSetInformationToken = NULL; + + + +/* + * HookedNtAdjustPrivilegesToken() + * + * Description: + * This function mediates the NtAdjustPrivilegesToken() system service and XXX. + * + * NOTE: ZwAdjustPrivilegesToken adjusts the attributes of the privileges in a token. [NAR] + * + * Parameters: + * Those of NtAdjustPrivilegesToken(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtAdjustPrivilegesToken(). + */ + +NTSTATUS +NTAPI +HookedNtAdjustPrivilegesToken +( + IN HANDLE TokenHandle, + IN BOOLEAN DisableAllPrivileges, + IN PTOKEN_PRIVILEGES NewState, + IN ULONG BufferLength, + OUT PTOKEN_PRIVILEGES PreviousState OPTIONAL, + OUT PULONG ReturnLength +) +{ + PCHAR FunctionName = "HookedNtAdjustPrivilegesToken"; + PCHAR TOKENNAME = NULL; /* allow the use of POLICY_CHECK_OPTYPE_NAME() macro */ + ULONG i; + + + HOOK_ROUTINE_ENTER(); + +/* + if (LearningMode == FALSE && IsTokenModificationAllowed() == FALSE) + { + LOG(LOG_SS_TOKEN, LOG_PRIORITY_DEBUG, ("%d (%S) HookedNtAdjustPrivilegesToken: disallowing token modification\n", (ULONG) PsGetCurrentProcessId(), GetCurrentProcessName())); + + LogAlert(ALERT_SS_TOKEN, OP_MODIFY, ALERT_RULE_NONE, ACTION_DENY, ALERT_PRIORITY_MEDIUM, NULL, 0, NULL); + + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } +*/ + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(TOKEN, OP_TOKEN_MODIFY); + } + + + if (KeGetPreviousMode() != KernelMode && DisableAllPrivileges == FALSE && ARGUMENT_PRESENT(NewState)) + { + BOOLEAN CaughtException; + + __try + { + // Probe to make sure the first ULONG (PrivilegeCount) is accessible + ProbeForRead(NewState, sizeof(ULONG), sizeof(ULONG)); + + // Now probe the entire structure + ProbeForRead(NewState, sizeof(TOKEN_PRIVILEGES) + + (NewState->PrivilegeCount - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES), + sizeof(ULONG)); + } + + __except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + + LOG(LOG_SS_TOKEN, LOG_PRIORITY_DEBUG, ("HookedNtAdjustPrivilegesToken(): caught an exception. status = 0x%x\n", status)); + + CaughtException = TRUE; + } + + + LOG(LOG_SS_TOKEN, LOG_PRIORITY_VERBOSE, ("%d HookedNtAdjustPrivilegesToken: %S\n", (ULONG) PsGetCurrentProcessId(), GetCurrentProcessName())); + + + //XXX replace with PID lookup +/* + if (CaughtException == FALSE && + wcsstr(GetCurrentProcessName(), L"svchost.exe") == 0 && + wcsstr(GetCurrentProcessName(), L"services.exe") == 0) + { + LOG(LOG_SS_TOKEN, LOG_PRIORITY_DEBUG, ("%d HookedNtAdjustPrivilegesToken\n", (ULONG) PsGetCurrentProcessId())); + + for (i = 0; i < NewState->PrivilegeCount; i++) + { + if (NewState->Privileges[i].Luid.LowPart == SE_AUDIT_PRIVILEGE && NewState->Privileges[i].Luid.HighPart == 0) + ; + else + KdPrint(("priv %d: %x %x %x\n", i, NewState->Privileges[i].Attributes, NewState->Privileges[i].Luid.LowPart, NewState->Privileges[i].Luid.HighPart)); + } + } +*/ + } + + + ASSERT(OriginalNtAdjustPrivilegesToken); + + rc = OriginalNtAdjustPrivilegesToken(TokenHandle, DisableAllPrivileges, NewState, BufferLength, + PreviousState, ReturnLength); + + + if (LearningMode == TRUE) + { + AddRule(RULE_TOKEN, NULL, OP_TOKEN_MODIFY); + } + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * HookedNtSetInformationToken() + * + * Description: + * This function mediates the NtSetInformationToken() system service and XXX. + * + * NOTE: ZwSetInformationToken sets information affecting a token object. [NAR] + * + * Parameters: + * Those of NtSetInformationToken(). + * + * Returns: + * STATUS_ACCESS_DENIED if the call does not pass the security policy check. + * Otherwise, NTSTATUS returned by NtSetInformationToken(). + */ + +NTSTATUS +NTAPI +HookedNtSetInformationToken +( + IN HANDLE TokenHandle, + IN TOKEN_INFORMATION_CLASS TokenInformationClass, + IN PVOID TokenInformation, + IN ULONG TokenInformationLength +) +{ + PCHAR FunctionName = "HookedNtSetInformationToken"; + PCHAR TOKENNAME = NULL; /* allow the use of POLICY_CHECK_OPTYPE_NAME() macro */ + + + HOOK_ROUTINE_ENTER(); + + + LOG(LOG_SS_TOKEN, LOG_PRIORITY_VERBOSE, ("%d HookedNtSetInformationToken %S %x %x %x\n", (ULONG) PsGetCurrentProcessId(), GetCurrentProcessName(), TokenInformationClass, TokenInformation, TokenInformationLength)); + +/* + if (LearningMode == FALSE && IsTokenModificationAllowed() == FALSE) + { + LOG(LOG_SS_TOKEN, LOG_PRIORITY_DEBUG, ("%d (%S) HookedNtSetInformationToken: disallowing token modification\n", (ULONG) PsGetCurrentProcessId(), GetCurrentProcessName())); + + LogAlert(ALERT_SS_TOKEN, OP_MODIFY, ALERT_RULE_NONE, ACTION_DENY, ALERT_PRIORITY_MEDIUM, NULL, 0, NULL); + + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } +*/ + if (LearningMode == FALSE) + { + POLICY_CHECK_OPTYPE_NAME(TOKEN, OP_TOKEN_MODIFY); + } + + + ASSERT(OriginalNtSetInformationToken); + + rc = OriginalNtSetInformationToken(TokenHandle, TokenInformationClass, TokenInformation, TokenInformationLength); + + + if (LearningMode == TRUE) + { + AddRule(RULE_TOKEN, NULL, OP_TOKEN_MODIFY); + } + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * InitTokenHooks() + * + * Description: + * Initializes all the mediated token object operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitTokenHooks() +{ + if ( (OriginalNtAdjustPrivilegesToken = (fpZwAdjustPrivilegesToken) ZwCalls[ZW_ADJUST_TOKEN_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_TOKEN, LOG_PRIORITY_DEBUG, ("InitTokenHooks: OriginalNtAdjustPrivilegesToken is NULL\n")); + return FALSE; + } + + if ( (OriginalNtSetInformationToken = (fpZwSetInformationToken) ZwCalls[ZW_SET_INFO_TOKEN_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_TOKEN, LOG_PRIORITY_DEBUG, ("InitTokenHooks: OriginalNtSetInformationToken is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/token.h b/token.h new file mode 100644 index 0000000..732ff56 --- /dev/null +++ b/token.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * token.h + * + * Abstract: + * + * This module defines various types used by token hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 25-Mar-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __TOKEN_H__ +#define __TOKEN_H__ + + +#include +#include "policy.h" +#include "pathproc.h" +#include "hookproc.h" +#include "procname.h" +#include "learn.h" +#include "log.h" + + +/* +ZwAdjustGroupsToken +ZwCreateToken +ZwOpenProcessToken +ZwOpenProcessTokenEx +ZwOpenThreadToken +ZwOpenThreadTokenEx +*/ + + +typedef struct _TOKEN_PRIVILEGES { + DWORD PrivilegeCount; + LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; +} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES; + + +/* + * ZwAdjustPrivilegesToken adjusts the attributes of the privileges in a token. [NAR] + */ + +typedef NTSTATUS (*fpZwAdjustPrivilegesToken) ( + IN HANDLE TokenHandle, + IN BOOLEAN DisableAllPrivileges, + IN PTOKEN_PRIVILEGES NewState, + IN ULONG BufferLength, + OUT PTOKEN_PRIVILEGES PreviousState OPTIONAL, + OUT PULONG ReturnLength + ); + +NTSTATUS +NTAPI +HookedNtAdjustPrivilegesToken( + IN HANDLE TokenHandle, + IN BOOLEAN DisableAllPrivileges, + IN PTOKEN_PRIVILEGES NewState, + IN ULONG BufferLength, + OUT PTOKEN_PRIVILEGES PreviousState OPTIONAL, + OUT PULONG ReturnLength + ); + + +/* + * ZwSetInformationToken sets information affecting a token object. [NAR] + */ + +typedef NTSTATUS (*fpZwSetInformationToken) ( + IN HANDLE TokenHandle, + IN TOKEN_INFORMATION_CLASS TokenInformationClass, + IN PVOID TokenInformation, + IN ULONG TokenInformationLength + ); + +NTSTATUS +NTAPI +HookedNtSetInformationToken( + IN HANDLE TokenHandle, + IN TOKEN_INFORMATION_CLASS TokenInformationClass, + IN PVOID TokenInformation, + IN ULONG TokenInformationLength + ); + + +/* + * ZwOpenProcessToken opens the token of a process. [NAR] + */ + +NTSYSAPI +NTSTATUS +NTAPI +ZwOpenProcessToken( + IN HANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + OUT PHANDLE TokenHandle + ); + + +/* + * ZwOpenThreadToken opens the token of a thread. [NAR] + */ + +NTSYSAPI +NTSTATUS +NTAPI +ZwOpenThreadToken( + IN HANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN BOOLEAN OpenAsSelf, + OUT PHANDLE TokenHandle + ); + + +/* + * ZwQueryInformationToken retrieves information about a token object. [NAR] + */ + +NTSYSAPI +NTSTATUS +NTAPI +ZwQueryInformationToken( + IN HANDLE TokenHandle, + IN TOKEN_INFORMATION_CLASS TokenInformationClass, + OUT PVOID TokenInformation, + IN ULONG TokenInformationLength, + OUT PULONG ReturnLength + ); + + +BOOLEAN InitTokenHooks(); + + +#endif /* __TOKEN_H__ */ diff --git a/userland.c b/userland.c new file mode 100644 index 0000000..805a413 --- /dev/null +++ b/userland.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * userland.c + * + * Abstract: + * + * This module defines various types routines used to interact with userland agent service. + * + * Author: + * + * Eugene Tsyrklevich 18-Apr-2004 + * + * Revision History: + * + * None. + */ + + +#include "userland.h" +#include "procname.h" +#include "process.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitUserland) +#pragma alloc_text (PAGE, ShutdownUserland) +#pragma alloc_text (PAGE, UserlandPostBootup) +#endif + + +BOOLEAN ActiveUserAgent = FALSE; + +PKEVENT UserlandRequestUserEvent = NULL; +HANDLE UserlandRequestUserEventHandle = NULL; + +PUSERLAND_REQUEST_HEADER UserlandRequestList = NULL; +KSPIN_LOCK gUserlandRequestListSpinLock; + +BOOLEAN CacheSid = TRUE; +ULONG CachedSidSize = 0, CachedSidReplySize = 0; +PVOID CachedSid = NULL; +PSID_RESOLVE_REPLY CachedSidReply = NULL; + +UCHAR SeqId = 0; /* global sequence id used for matching up userland requests & replies */ + + + +/* + * UserlandPostBootup() + * + * Description: + * Initializes userland related data once computer is done booting up. + * + * \BaseNamedObjects object directory is not created until the Win32® system initializes. + * Therefore, drivers that are loaded at boot time cannot create event objects in their DriverEntry + * routines that are visible to user-mode programs. + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +UserlandPostBootup() +{ + UNICODE_STRING uszUserlandRequestEvent; + + + ASSERT(BootingUp == FALSE); + + + KeInitializeSpinLock(&gUserlandRequestListSpinLock); + + + /* Create an event for userland agent service to monitor */ +#define USERLAND_REQUEST_EVENT_NAME L"\\BaseNamedObjects\\OzoneUserlandRequestEvent" + + RtlInitUnicodeString(&uszUserlandRequestEvent, USERLAND_REQUEST_EVENT_NAME); + + + if ((UserlandRequestUserEvent = IoCreateNotificationEvent(&uszUserlandRequestEvent, &UserlandRequestUserEventHandle)) == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("UserlandPostBootup: IoCreateNotificationEvent failed\n")); + return FALSE; + } + + KeClearEvent(UserlandRequestUserEvent); + + + return TRUE; +} + + + +/* + * IssueUserlandSidResolveRequest() + * + * Description: + * Resolves binary SID to its ASCII representation by querying a userland agent. + * + * Parameters: + * . + * + * Returns: + * TRUE to indicate success, FALSE otherwise. + */ + +BOOLEAN +IssueUserlandSidResolveRequest(PIMAGE_PID_ENTRY Process) +{ + PSID_RESOLVE_REQUEST pSidResolveRequest; + USHORT Size; + PCHAR UserSidBuffer; + KIRQL irql; + LARGE_INTEGER liDelay; + NTSTATUS status; + + + ASSERT(Process); + + + if (!ActiveUserAgent) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_VERBOSE, ("IssueUserlandSidResolveRequest: no Agent Service\n")); + return FALSE; + } + + if (KeGetCurrentIrql() != PASSIVE_LEVEL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: Running at high IRQL\n")); + return FALSE; + } + + if (Process->ProcessId == SystemProcessId) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: Cannot issue requests on behalf of SYSTEM process\n")); +// return FALSE; + } + + + /* + * the following code assumes that SID_RESOLVE_REQUESTS consists of USERLAND_REQUEST_HEADER + * followed by PSID_AND_ATTRIBUTES. + * + * GetCurrentUserSid() allocates memory for user sid + PSID_AND_ATTRIBUTES and converts SID + * from absolute into relative format. + */ + + Size = sizeof(USERLAND_REQUEST_HEADER); + UserSidBuffer = GetCurrentUserSid(&Size); + + if (UserSidBuffer == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: out of memory\n")); + return FALSE; + } + + /* check for a previously resolved cached SID */ + while (CacheSid == TRUE) + { + int SidSize = Size - sizeof(USERLAND_REQUEST_HEADER); + + + /* cache hit? */ + if (CachedSid && CachedSidReply && + SidSize == CachedSidSize && + memcmp(UserSidBuffer + sizeof(USERLAND_REQUEST_HEADER), CachedSid, CachedSidSize) == 0) + { + Process->WaitingForUserRequestId = 0; + Process->UserlandReply = ExAllocatePoolWithTag(PagedPool, CachedSidReplySize, _POOL_TAG); + + if (Process->UserlandReply == NULL) + { + ExFreePoolWithTag(UserSidBuffer, _POOL_TAG); + return FALSE; + } + + memcpy(Process->UserlandReply, CachedSidReply, CachedSidReplySize); + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: Cache hit. Returning username %S\n", CachedSidReply->UserName)); + + ExFreePoolWithTag(UserSidBuffer, _POOL_TAG); + + return TRUE; + } + + + /* cache miss */ + CachedSidSize = SidSize; + + if (CachedSid) + ExFreePoolWithTag(CachedSid, _POOL_TAG); + + CachedSid = ExAllocatePoolWithTag(PagedPool, SidSize, _POOL_TAG); + + if (CachedSid == NULL) + { + ExFreePoolWithTag(UserSidBuffer, _POOL_TAG); + return FALSE; + } + + memcpy(CachedSid, UserSidBuffer + sizeof(USERLAND_REQUEST_HEADER), SidSize); + + if (CachedSidReply) + { + ExFreePoolWithTag(CachedSidReply, _POOL_TAG); + CachedSidReply = NULL; + } + + break; + } + + + pSidResolveRequest = (PSID_RESOLVE_REQUEST) UserSidBuffer; + + + pSidResolveRequest->RequestHeader.RequestType = USERLAND_SID_RESOLVE_REQUEST; + pSidResolveRequest->RequestHeader.RequestSize = Size; + pSidResolveRequest->RequestHeader.ProcessId = Process->ProcessId; + + if (++SeqId == 0) SeqId = 1; + pSidResolveRequest->RequestHeader.SeqId = SeqId; + + + KeAcquireSpinLock(&gUserlandRequestListSpinLock, &irql); + { + pSidResolveRequest->RequestHeader.Next = UserlandRequestList; + UserlandRequestList = (PUSERLAND_REQUEST_HEADER) pSidResolveRequest; + } + KeReleaseSpinLock(&gUserlandRequestListSpinLock, irql); + + + Process->WaitingForUserRequestId = SeqId; + Process->UserlandReply = NULL; + + + KeClearEvent(&Process->UserlandRequestDoneEvent); + + + /* signal the userland agent service */ + if (UserlandRequestUserEvent) + { + /* pulse twice since userland sometimes misses single pulses */ + KeSetEvent(UserlandRequestUserEvent, 0, FALSE); + KeClearEvent(UserlandRequestUserEvent); + + KeSetEvent(UserlandRequestUserEvent, 0, FALSE); + KeClearEvent(UserlandRequestUserEvent); + } + else + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: UserlandRequestUserEvent is NULL\n")); + } + + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%d IssueUserlandSidResolveRequest: Waiting for agent service\n", CURRENT_PROCESS_PID)); + + + /* wait for the userland service to reply (wait for maximum of USERLAND_REQUEST_TIMEOUT seconds) */ + liDelay.QuadPart = SECONDS(USERLAND_REQUEST_TIMEOUT); + + status = KeWaitForSingleObject(&Process->UserlandRequestDoneEvent, UserRequest, KernelMode, FALSE, &liDelay); + + + Process->WaitingForUserRequestId = 0; + + if (status == STATUS_TIMEOUT) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: KeWaitForSingleObject timed out\n")); + } + else + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: KeWaitForSingleObject returned\n")); + + /* cache the resolved Sid */ + if (CacheSid == TRUE && Process->UserlandReply != NULL) + { + CachedSidReply = ExAllocatePoolWithTag(PagedPool, Process->UserlandReply->ReplySize, _POOL_TAG); + + if (CachedSidReply) + { + memcpy(CachedSidReply, Process->UserlandReply, Process->UserlandReply->ReplySize); + + CachedSidReplySize = Process->UserlandReply->ReplySize; + } + } + } + + + /* at this point UserSidBuffer/pSidResolveRequest has already been deallocated in driver.c!DriverDeviceControl */ + + + return TRUE; +} + + + +/* + * IssueUserlandAskUserRequest() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * . + */ + +ACTION_TYPE +IssueUserlandAskUserRequest(RULE_TYPE RuleType, UCHAR OperationType, PCHAR ObjectName) +{ + ACTION_TYPE Action = ACTION_DENY_DEFAULT; + PIMAGE_PID_ENTRY Process; + PASK_USER_REQUEST pAskUserRequest; + USHORT Size; + KIRQL irql; + LARGE_INTEGER liDelay; + NTSTATUS status; + +#define NAME_BUFFER_SIZE 256 + ANSI_STRING asObjectName; + UNICODE_STRING usObjectName; + WCHAR ObjectNameW[NAME_BUFFER_SIZE] = { 0 }; + + + ASSERT(ObjectName); + + + if (!ActiveUserAgent) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: no Agent Service\n")); + return Action; + } + + if (KeGetCurrentIrql() != PASSIVE_LEVEL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: Running at high IRQL\n")); + return FALSE; + } + + + if (CURRENT_PROCESS_PID == SystemProcessId) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: Cannot issue requests on behalf of SYSTEM process\n")); + return FALSE; + } + + + Process = FindImagePidEntry(CURRENT_PROCESS_PID, 0); + if (Process == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: Process %d not found\n", CURRENT_PROCESS_PID)); + return Action; + } + + + /* \KnownDlls section rules need to be converted to DLLs */ + if (RuleType == RULE_SECTION) + { + if (_strnicmp(ObjectName, "\\KnownDlls\\", 11) == 0) + { + ObjectName += 11; + RuleType = RULE_DLL; + OperationType = OP_LOAD; + } + } + + + ObjectName = FilterObjectName(ObjectName); + + usObjectName.Length = 0; + usObjectName.MaximumLength = sizeof(ObjectNameW); + usObjectName.Buffer = ObjectNameW; + + RtlInitAnsiString(&asObjectName, ObjectName); + + RtlAnsiStringToUnicodeString(&usObjectName, &asObjectName, FALSE); + ObjectNameW[ asObjectName.Length ] = 0; + + + /* extra +1 for ProcessName zero termination */ + Size = sizeof(ASK_USER_REQUEST) + (wcslen(ObjectNameW) + wcslen(Process->ImageName) + 1) * sizeof(WCHAR); + + pAskUserRequest = ExAllocatePoolWithTag(NonPagedPool, Size, _POOL_TAG); + + if (pAskUserRequest == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: out of memory\n")); + return Action; + } + + + pAskUserRequest->RequestHeader.RequestType = USERLAND_ASK_USER_REQUEST; + pAskUserRequest->RequestHeader.RequestSize = Size; + pAskUserRequest->RequestHeader.ProcessId = Process->ProcessId; + + if (++SeqId == 0) SeqId = 1; + pAskUserRequest->RequestHeader.SeqId = SeqId; + + pAskUserRequest->RuleType = RuleType; + pAskUserRequest->OperationType = OperationType; + pAskUserRequest->ObjectNameLength = (USHORT) wcslen(ObjectNameW); + pAskUserRequest->ProcessNameLength = (USHORT) wcslen(Process->ImageName); + + wcscpy(pAskUserRequest->ObjectName, ObjectNameW); + + /* process name follows the object name */ + wcscpy(pAskUserRequest->ObjectName + pAskUserRequest->ObjectNameLength + 1, Process->ImageName); + + + KeAcquireSpinLock(&gUserlandRequestListSpinLock, &irql); + { + pAskUserRequest->RequestHeader.Next = UserlandRequestList; + UserlandRequestList = (PUSERLAND_REQUEST_HEADER) pAskUserRequest; + } + KeReleaseSpinLock(&gUserlandRequestListSpinLock, irql); + + + Process->WaitingForUserRequestId = SeqId; + Process->UserlandReply = NULL; + + + KeClearEvent(&Process->UserlandRequestDoneEvent); + + + /* signal the userland agent service */ + if (UserlandRequestUserEvent) + { + /* pulse twice since userland sometimes misses single pulses */ + KeSetEvent(UserlandRequestUserEvent, 0, FALSE); + KeClearEvent(UserlandRequestUserEvent); + + KeSetEvent(UserlandRequestUserEvent, 0, FALSE); + KeClearEvent(UserlandRequestUserEvent); + } + else + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: UserlandRequestUserEvent is NULL\n")); + } + + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%d IssueUserlandAskUserRequest: Waiting for agent service\n", CURRENT_PROCESS_PID)); + + + /* wait for the user/userland service to reply (wait for maximum of USERLAND_REQUEST_TIMEOUT seconds) */ + liDelay.QuadPart = SECONDS(USERLAND_REQUEST_TIMEOUT); + + status = KeWaitForSingleObject(&Process->UserlandRequestDoneEvent, UserRequest, KernelMode, FALSE, &liDelay); + + + Process->WaitingForUserRequestId = 0; + + if (status != STATUS_TIMEOUT) + { + PASK_USER_REPLY pAskUserReply = (PASK_USER_REPLY) Process->UserlandReply; + + if (pAskUserReply) + { + Action = pAskUserReply->Action; + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: received back action %d\n", Action)); + + ExFreePoolWithTag(Process->UserlandReply, _POOL_TAG); + Process->UserlandReply = NULL; + } + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: IssueUserlandAskUserRequest returned\n")); + } + else + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: KeWaitForSingleObject timed out\n")); + + //XXX need to remove pAskUserRequest from the list + } + + + /* at this point pAskUserRequest has already been deallocated in driver.c!DriverDeviceControl */ + + + return Action; +} + + + +/* + * InitUserland() + * + * Description: + * Initializes all the userland related data. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitUserland() +{ + if (BootingUp == FALSE) + { + if (UserlandPostBootup() == FALSE) + + return FALSE; + } + + + return TRUE; +} + + + +/* + * ShutdownUserland() + * + * Description: + * XXX. + * + * NOTE: Called once during driver unload (DriverUnload()). + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +VOID +ShutdownUserland() +{ + PUSERLAND_REQUEST_HEADER tmp; + KIRQL irql; + + + if (UserlandRequestUserEventHandle) + ZwClose(UserlandRequestUserEventHandle); + + + KeAcquireSpinLock(&gUserlandRequestListSpinLock, &irql); + { + while (UserlandRequestList) + { + tmp = UserlandRequestList; + UserlandRequestList = UserlandRequestList->Next; + + ExFreePoolWithTag(tmp, _POOL_TAG); + } + } + KeReleaseSpinLock(&gUserlandRequestListSpinLock, irql); + + + if (CachedSid) + { + ExFreePoolWithTag(CachedSid, _POOL_TAG); + CachedSid = NULL; + } + + if (CachedSidReply) + { + ExFreePoolWithTag(CachedSidReply, _POOL_TAG); + CachedSidReply = NULL; + } +} diff --git a/userland.h b/userland.h new file mode 100644 index 0000000..03764fb --- /dev/null +++ b/userland.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * userland.h + * + * Abstract: + * + * This module defines various types used by userland interacting routines. + * + * Author: + * + * Eugene Tsyrklevich 18-Apr-2004 + * + * Revision History: + * + * None. + */ + +#ifndef __USERLAND_H__ +#define __USERLAND_H__ + + +#include +#include "policy.h" +#include "misc.h" + + +/* number of seconds to wait for userland agent to reply */ +#define USERLAND_REQUEST_TIMEOUT 5 + + +#define USERLAND_SID_RESOLVE_REQUEST 1 +#define USERLAND_ASK_USER_REQUEST 2 + + +/* + * all userland requests start with the following header + */ + +typedef struct _USERLAND_REQUEST_HEADER +{ + struct _USERLAND_REQUEST_HEADER *Next; + + USHORT RequestType; + USHORT RequestSize; + ULONG ProcessId; + UCHAR SeqId; /* Sequence id, will roll over but that's fine */ + +} USERLAND_REQUEST_HEADER, *PUSERLAND_REQUEST_HEADER; + + +/* binary SID -> ASCII name resolve request */ + +typedef struct _SID_RESOLVE_REQUEST +{ + USERLAND_REQUEST_HEADER RequestHeader; + PSID_AND_ATTRIBUTES PUserSidAndAttributes; + +} SID_RESOLVE_REQUEST, *PSID_RESOLVE_REQUEST; + + +/* Ask user request */ + +typedef struct _ASK_USER_REQUEST +{ + USERLAND_REQUEST_HEADER RequestHeader; + RULE_TYPE RuleType; + UCHAR OperationType; + USHORT ObjectNameLength; + USHORT ProcessNameLength; + + WCHAR ObjectName[ANYSIZE_ARRAY]; + + /* ProcessName follows the zero-terminated ObjectName */ +// WCHAR ProcessName[ANYSIZE_ARRAY]; + +} ASK_USER_REQUEST, *PASK_USER_REQUEST; + + + +/* + * all userland replies start with the following header + */ + +typedef struct _USERLAND_REPLY_HEADER +{ + ULONG ProcessId; + USHORT ReplySize; + UCHAR SeqId; /* Sequence id, will roll over but that's fine */ + +} USERLAND_REPLY_HEADER, *PUSERLAND_REPLY_HEADER; + + +/* binary SID -> ASCII name resolve reply */ + +typedef struct _SID_RESOLVE_REPLY +{ + USERLAND_REPLY_HEADER ReplyHeader; + USHORT UserNameLength; + WCHAR UserName[ANYSIZE_ARRAY]; + +} SID_RESOLVE_REPLY, *PSID_RESOLVE_REPLY; + + +/* Ask user reply */ + +typedef struct _ASK_USER_REPLY +{ + USERLAND_REPLY_HEADER ReplyHeader; + ACTION_TYPE Action; + +} ASK_USER_REPLY, *PASK_USER_REPLY; + + +extern BOOLEAN ActiveUserAgent; +extern PUSERLAND_REQUEST_HEADER UserlandRequestList; +extern KSPIN_LOCK gUserlandRequestListSpinLock; +extern PKEVENT UserlandRequestUserEvent; + + +BOOLEAN InitUserland(); +BOOLEAN UserlandPostBootup(); +VOID ShutdownUserland(); + +typedef struct _IMAGE_PID_ENTRY *PIMAGE_PID_ENTRY; + +BOOLEAN IssueUserlandSidResolveRequest(PIMAGE_PID_ENTRY Process); +ACTION_TYPE IssueUserlandAskUserRequest(RULE_TYPE RuleType, UCHAR OperationType, PCHAR ObjectName); + + +#endif /* __USERLAND_H__ */ \ No newline at end of file diff --git a/vdm.c b/vdm.c new file mode 100644 index 0000000..e445d2f --- /dev/null +++ b/vdm.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * vdm.c + * + * Abstract: + * + * This module implements various VDM (Virtual Dos Machine) hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 06-Apr-2004 + * + * Revision History: + * + * None. + */ + + +#include +#include "vdm.h" +#include "policy.h" +#include "hookproc.h" +#include "procname.h" +#include "policy.h" +#include "learn.h" +#include "log.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitVdmHooks) +#endif + + +fpZwSetLdtEntries OriginalNtSetLdtEntries = NULL; +fpZwVdmControl OriginalNtVdmControl = NULL; + + + +/* + * IsVdmAllowed() + * + * Description: + * Check whether the current process is allowed to use dos16/VDM functionality. + * + * Parameters: + * None. + * + * Returns: + * FALSE if VDM is disabled. TRUE otherwise. + */ + +BOOLEAN +IsVdmAllowed() +{ + PIMAGE_PID_ENTRY CurrentProcess; + BOOLEAN VdmAllowed = FALSE; + + + /* check the global policy first */ + if (! IS_VDM_PROTECTION_ON(gSecPolicy)) + return TRUE; + + + /* now check the process specific policy */ + CurrentProcess = FindImagePidEntry(CURRENT_PROCESS_PID, 0); + + if (CurrentProcess != NULL) + { + VdmAllowed = ! IS_VDM_PROTECTION_ON(CurrentProcess->SecPolicy); + } + else + { + LOG(LOG_SS_VDM, LOG_PRIORITY_DEBUG, ("%d IsVdmAllowed: CurrentProcess = NULL!\n", CURRENT_PROCESS_PID)); + } + + + return VdmAllowed; +} + + + +/* + * HookedNtSetLdtEntries() + * + * Description: + * This function mediates the NtSetLdtEntries() system service and disallows access to it. + * + * NOTE: ZwSetLdtEntries sets Local Descriptor Table (LDT) entries for a Virtual DOS Machine (VDM). [NAR] + * + * Parameters: + * Those of NtSetLdtEntries(). + * + * Returns: + * STATUS_ACCESS_DENIED if 16-bit applications are disabled. + * Otherwise, NTSTATUS returned by NtSetLdtEntries(). + */ + +NTSTATUS +NTAPI +HookedNtSetLdtEntries +( + IN ULONG Selector0, + IN ULONG Entry0Low, + IN ULONG Entry0Hi, + IN ULONG Selector1, + IN ULONG Entry1Low, + IN ULONG Entry1Hi +) +{ + HOOK_ROUTINE_ENTER(); + + + LOG(LOG_SS_VDM, LOG_PRIORITY_VERBOSE, ("%d (%S) HookedNtSetLdtEntries(%x %x %x)\n", (ULONG) PsGetCurrentProcessId(), GetCurrentProcessName(), Selector0, Entry0Low, Entry0Hi)); + + if (LearningMode == FALSE && IsVdmAllowed() == FALSE) + { + LOG(LOG_SS_VDM, LOG_PRIORITY_DEBUG, ("%d (%S) HookedNtSetLdtEntries: disallowing VDM access\n", (ULONG) PsGetCurrentProcessId(), GetCurrentProcessName())); + + LogAlert(ALERT_SS_VDM, OP_VDM_USE, ALERT_RULE_NONE, ACTION_DENY, ALERT_PRIORITY_MEDIUM, NULL, 0, NULL); + + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + ASSERT(OriginalNtSetLdtEntries); + + rc = OriginalNtSetLdtEntries(Selector0, Entry0Low, Entry0Hi, Selector1, Entry1Low, Entry1Hi); + + + if (LearningMode == TRUE) + TURN_VDM_PROTECTION_OFF(NewPolicy); + + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * HookedNtVdmControl() + * + * Description: + * This function mediates the NtVdmControl() system service and disallows access to it. + * + * NOTE: ZwVdmControl performs a control operation on a VDM. [NAR] + * + * Parameters: + * Those of NtVdmControl(). + * + * Returns: + * STATUS_ACCESS_DENIED if 16-bit applications are disabled. + * Otherwise, NTSTATUS returned by NtVdmControl(). + */ + +NTSTATUS +NTAPI +HookedNtVdmControl +( + IN ULONG ControlCode, + IN PVOID ControlData +) +{ + HOOK_ROUTINE_ENTER(); + + + LOG(LOG_SS_VDM, LOG_PRIORITY_VERBOSE, ("%d (%S) HookedNtVdmControl(%x %x)\n", (ULONG) PsGetCurrentProcessId(), GetCurrentProcessName(), ControlCode, ControlData)); + + if (LearningMode == FALSE && IsVdmAllowed() == FALSE) + { + LOG(LOG_SS_VDM, LOG_PRIORITY_DEBUG, ("%d (%S) HookedNtVdmControl: disallowing VDM access\n", (ULONG) PsGetCurrentProcessId(), GetCurrentProcessName())); + + LogAlert(ALERT_SS_VDM, OP_VDM_USE, ALERT_RULE_NONE, ACTION_DENY, ALERT_PRIORITY_MEDIUM, NULL, 0, NULL); + + HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); + } + + + ASSERT(OriginalNtVdmControl); + + rc = OriginalNtVdmControl(ControlCode, ControlData); + + + if (LearningMode == TRUE) + TURN_VDM_PROTECTION_OFF(NewPolicy); + + + HOOK_ROUTINE_EXIT(rc); +} + + + +/* + * InitVdmHooks() + * + * Description: + * Initializes all the mediated vdm operation pointers. The "OriginalFunction" pointers + * are initialized by InstallSyscallsHooks() that must be called prior to this function. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitVdmHooks() +{ + if ( (OriginalNtSetLdtEntries = (fpZwSetLdtEntries) ZwCalls[ZW_SET_LDT_ENTRIES_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_VDM, LOG_PRIORITY_DEBUG, ("InitVdmHooks: OriginalNtSetLdtEntries is NULL\n")); + return FALSE; + } + + if ( (OriginalNtVdmControl = (fpZwVdmControl) ZwCalls[ZW_VDM_CONTROL_INDEX].OriginalFunction) == NULL) + { + LOG(LOG_SS_VDM, LOG_PRIORITY_DEBUG, ("InitVdmHooks: OriginalNtVdmControl is NULL\n")); + return FALSE; + } + + return TRUE; +} diff --git a/vdm.h b/vdm.h new file mode 100644 index 0000000..260a05e --- /dev/null +++ b/vdm.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * vdm.h + * + * Abstract: + * + * This module implements various VDM (Virtual Dos Machine) hooking routines. + * + * Author: + * + * Eugene Tsyrklevich 06-Apr-2004 + * + * Revision History: + * + * None. + */ + + +#ifndef __VDM_H__ +#define __VDM_H__ + + + +/* + * ZwSetLdtEntries sets Local Descriptor Table (LDT) entries for a Virtual DOS Machine (VDM). [NAR] + */ + +typedef NTSTATUS (*fpZwSetLdtEntries) ( + IN ULONG Selector0, + IN ULONG Entry0Low, + IN ULONG Entry0Hi, + IN ULONG Selector1, + IN ULONG Entry1Low, + IN ULONG Entry1Hi + ); + +NTSTATUS +NTAPI +HookedNtSetLdtEntries( + IN ULONG Selector0, + IN ULONG Entry0Low, + IN ULONG Entry0Hi, + IN ULONG Selector1, + IN ULONG Entry1Low, + IN ULONG Entry1Hi + ); + + +/* + * ZwVdmControl performs a control operation on a VDM. [NAR] + */ + +typedef NTSTATUS (*fpZwVdmControl) ( + IN ULONG ControlCode, + IN PVOID ControlData + ); + +NTSTATUS +NTAPI +HookedNtVdmControl( + IN ULONG ControlCode, + IN PVOID ControlData + ); + + +BOOLEAN InitVdmHooks(); + + +#endif /* __VDM_H__ */ diff --git a/wireless.c b/wireless.c new file mode 100644 index 0000000..0132824 --- /dev/null +++ b/wireless.c @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * wireless.c + * + * Abstract: + * + * This module deals with wireless cards. + * + * Author: + * + * Eugene Tsyrklevich 12-Oct-2004 + */ + + +#include + +#undef DEFINE_GUID +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + +#include +#include +#include "wireless.h" +#include "pathproc.h" +#include "policy.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitWirelessHooks) +#pragma alloc_text (PAGE, RemoveWirelessHooks) +#endif + + +PVOID WirelessNotificationEntry = NULL; + +/* removable wireless flags defined in drive.h (READONLY, etc) */ +UCHAR WirelessRemovableFlags = 0; + + +typedef struct _WORK_CONTEXT +{ + PIO_WORKITEM Item; + UNICODE_STRING SymbolicLinkName; + +} WORK_CONTEXT, *PWORK_CONTEXT; + + + +/* + * AddDrive() + * + * Description: + * . + * + * Parameters: + * pusDriveName - . + * DriveLetter - . + * + * Returns: + * Nothing. + */ + +VOID +AddDrive(PUNICODE_STRING pusDriveName, CHAR DriveLetter) +{ + PDEVICE_OBJECT pDeviceObject; + STORAGE_HOTPLUG_INFO HotplugInfo; + + + pDeviceObject = GetDriveHotplugInformation(pusDriveName, &HotplugInfo); + if (pDeviceObject == NULL) + return; + + + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("AddDrive: %c:\\ drive: %d %d %d %d %d\n", DriveLetter, HotplugInfo.Size, HotplugInfo.WirelessRemovable, HotplugInfo.WirelessHotplug, HotplugInfo.DeviceHotplug, HotplugInfo.WriteCacheEnableOverride)); + + + //XXX remove + if (HotplugInfo.WirelessRemovable == FALSE && HotplugInfo.WirelessHotplug == TRUE) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("AddDrive: hotpluggable but not removable drive! %c: %S\n", DriveLetter, pusDriveName->Buffer)); + } + + if (HotplugInfo.WirelessRemovable) + { + CHAR rule[MAX_PATH]; + + + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("AddDrive: removable drive! %c: %S\n", DriveLetter, pusDriveName->Buffer)); + + + /* Create a new global policy rule */ + + if (IS_REMOVABLE_WIRELESS_DISABLED()) + { + sprintf(rule, "name match \"%c:\\*\" then %s", DriveLetter, "deny"); + + PolicyParseObjectRule(&gSecPolicy, RULE_FILE, "all", rule); + + /* no need to process other rules, this one denies everything already */ + return; + } + + if (IS_REMOVABLE_WIRELESS_READONLY()) + { + sprintf(rule, "name match \"%c:\\*\" then %s", DriveLetter, "deny"); + + PolicyParseObjectRule(&gSecPolicy, RULE_FILE, "write", rule); + } + + if (IS_REMOVABLE_WIRELESS_NOEXECUTE()) + { + sprintf(rule, "name match \"%c:\\*\" then %s", DriveLetter, "deny"); + + PolicyParseObjectRule(&gSecPolicy, RULE_FILE, "execute", rule); + } + } + + + return; +} + + + +/* + * RemoveDrive() + * + * Description: + * . + * + * Parameters: + * pusDriveName - . + * + * Returns: + * Nothing. + */ + +VOID +RemoveDrive(PUNICODE_STRING pusDriveName) +{ + PDEVICE_OBJECT pDeviceObject; + STORAGE_HOTPLUG_INFO HotplugInfo; + NTSTATUS rc; + + + pDeviceObject = GetDriveHotplugInformation(pusDriveName, &HotplugInfo); + if (pDeviceObject == NULL) + return; + +// KdPrint(("success %d %d %d %d %d\n", info.Size, info.WirelessRemovable, info.WirelessHotplug, info.DeviceHotplug, info.WriteCacheEnableOverride)); + + if (HotplugInfo.WirelessRemovable) + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("RemoveDrive: removable drive! %S\n", pusDriveName->Buffer)); + + + rc = RtlVolumeDeviceToDosName(pDeviceObject, pusDriveName); + if (NT_SUCCESS(rc)) + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("RemoveDrive: IoVolumeDeviceToDosName returned %S\n", pusDriveName->Buffer)); + + return; +} + + + +/* + * PnpWorker() + * + * Description: + * A work item routine that runs at PASSIVE_LEVEL irql. The routine is scheduled by PnpCallback + * which is not allowed to block. + * + * PnpWorker calls RemoveDrive() with a drive name setup by PnpCallback. + * + * Parameters: + * pDeviceObject - . + * Context - . + * + * Returns: + * Nothing. + */ + +VOID +PnpWorker(IN PDEVICE_OBJECT pDeviceObject, IN PVOID Context) +{ + PWORK_CONTEXT WorkContext = (PWORK_CONTEXT) Context; + + + if (WorkContext == NULL) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("PnpWorker: WorkContext = NULL\n")); + return; + } + + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("PnpWorker: %S\n", WorkContext->SymbolicLinkName.Buffer)); + + + RemoveDrive(&WorkContext->SymbolicLinkName); + + + IoFreeWorkItem(WorkContext->Item); + + ExFreePoolWithTag(WorkContext, _POOL_TAG); +} + + + +/* + * PnpCallback() + * + * Description: + * Plug-and-Play callback. Gets called when a p-n-p drive/cdrom interface is modified + * (i.e. when a removable drive is added or removed from the system). + * + * Parameters: + * NotificationStructure - DEVICE_INTERFACE_CHANGE_NOTIFICATION indicating which interface changed. + * Context - the driver's device context. + * + * Returns: + * STATUS_SUCCESS. + */ + +NTSTATUS +PnpCallback(IN PVOID NotificationStructure, IN PVOID Context) +{ + PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notify = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION) NotificationStructure; + PDEVICE_OBJECT pDeviceObject = (PDEVICE_OBJECT) Context; + PIO_WORKITEM WorkItem; + PWORK_CONTEXT WorkContext; + + + if (IsEqualGUID((LPGUID) &Notify->Event, (LPGUID) &GUID_DEVICE_INTERFACE_REMOVAL)) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("GUID_DEVICE_INTERFACE_REMOVAL %S\n", Notify->SymbolicLinkName->Buffer)); +/* + } + + + if (IsEqualGUID((LPGUID) &Notify->Event, (LPGUID) &GUID_DEVICE_INTERFACE_ARRIVAL) || + IsEqualGUID((LPGUID) &Notify->Event, (LPGUID) &GUID_DEVICE_INTERFACE_REMOVAL)) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("GUID_DEVICE_INTERFACE_ARRIVAL %x %S\n", Notify->SymbolicLinkName, Notify->SymbolicLinkName->Buffer)); +*/ + + /* + * Schedule a work item to process this request. Cannot block in this callback function. + */ + + WorkItem = IoAllocateWorkItem(pDeviceObject); + + WorkContext = (PWORK_CONTEXT) ExAllocatePoolWithTag(PagedPool, + sizeof(WORK_CONTEXT) + Notify->SymbolicLinkName->Length, + _POOL_TAG); + if (!WorkContext) + { + IoFreeWorkItem(WorkItem); + return(STATUS_SUCCESS); + } + + WorkContext->SymbolicLinkName.Buffer = (PWSTR) ((PCHAR) &WorkContext->SymbolicLinkName + sizeof(UNICODE_STRING)); + WorkContext->SymbolicLinkName.MaximumLength = Notify->SymbolicLinkName->Length; + WorkContext->SymbolicLinkName.Length = 0; + + WorkContext->Item = WorkItem; + RtlCopyUnicodeString(&WorkContext->SymbolicLinkName, Notify->SymbolicLinkName); + + IoQueueWorkItem(WorkItem, PnpWorker, DelayedWorkQueue, WorkContext); + } + + + return STATUS_SUCCESS; +} + + + +/* + * InitWirelessHooks() + * + * Description: + * Process any existing wireless cards and register Plug-and-Play notifications for future drive additions/removals. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitWirelessHooks(IN PDRIVER_OBJECT pDriverObject, IN PDEVICE_OBJECT pDeviceObject) +{ + NTSTATUS rc; + + + rc = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, + /*0,*/PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (LPGUID) &GUID_DEVINTERFACE_DISK, + pDriverObject, + PnpCallback, + pDeviceObject, + &WirelessNotificationEntry); + + if (! NT_SUCCESS(rc)) + { + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("InitWirelessHooks: IoRegisterPlugPlayNotification failed with status %x\n", rc)); + return FALSE; + } + + + return TRUE; +} + + + +/* + * RemoveWirelessHooks() + * + * Description: + * Unregister Plug-and-Play notifications. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +VOID +RemoveWirelessHooks() +{ + if (WirelessNotificationEntry) + if (! NT_SUCCESS(IoUnregisterPlugPlayNotification(WirelessNotificationEntry))) + LOG(LOG_SS_DRIVE, LOG_PRIORITY_DEBUG, ("RemoveWirelessHooks: IoUnregisterPlugPlayNotification failed\n")); +} diff --git a/wireless.h b/wireless.h new file mode 100644 index 0000000..ac55368 --- /dev/null +++ b/wireless.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * wireless.c + * + * Abstract: + * + * This module deals with wireless cards. + * + * Author: + * + * Eugene Tsyrklevich 02-Jun-2004 + */ + + +#ifndef __WIRELESS_H__ +#define __WIRELESS_H__ + + +#define WIRELESS_REMOVABLE_PERMIT 0 +#define WIRELESS_REMOVABLE_DISABLE 1 +#define WIRELESS_REMOVABLE_READONLY 2 +#define WIRELESS_REMOVABLE_NOEXECUTE 4 + +#define IS_REMOVABLE_WIRELESS_READONLY() ((WirelessRemovableFlags & WIRELESS_REMOVABLE_READONLY) == WIRELESS_REMOVABLE_READONLY) +#define IS_REMOVABLE_WIRELESS_DISABLED() ((WirelessRemovableFlags & WIRELESS_REMOVABLE_DISABLE) == WIRELESS_REMOVABLE_DISABLE) +#define IS_REMOVABLE_WIRELESS_NOEXECUTE() ((WirelessRemovableFlags & WIRELESS_REMOVABLE_NOEXECUTE) == WIRELESS_REMOVABLE_NOEXECUTE) + + +extern UCHAR WirelessRemovableFlags; + + +BOOLEAN InitWirelessHooks(IN PDRIVER_OBJECT pDriverObject, IN PDEVICE_OBJECT pDeviceObject); +VOID RemoveRemovableWirelessHooks(); +VOID MonitorDriveLinks(const PCHAR Link); + + +#endif /* __WIRELESS_H__ */ -- cgit v1.3