A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about kernel-mode development.
 #27509  by kerpow1
 Sun Jan 03, 2016 10:02 am
Hi,

Wondering what other ways there are to load a driver which avoids using Service Control Manager.

Currently I use a script which executes dsefix, creates a service, enables service, then dsefix -e to unload thus avoiding test mode.

The problem with this is that is records events inside USN and Event Log as its part of services.exe and other things, so requires an event being logged.

Alternatives I know about;

ZwLoadDriver - This requires a service creation event as far as I know so wouldn't be ideal)
ZwSetSystemInformation (MmLoadSystemImage) - By using this with dsefix then I will bypass the integrity checks and driver should load fine without logging?

Whats your thoughts, any other avenues? How will PG deal with MmLoadSystemImage option?

Thanks
 #27524  by Vrtule
 Sun Jan 03, 2016 7:19 pm
Hello,
ZwLoadDriver - This requires a service creation event as far as I know so wouldn't be ideal)
You just need to create a service registry key for your driver. This is possible without any interaction with SCM. Just create the key, fill in several values and load the driver.
ZwSetSystemInformation (MmLoadSystemImage) - By using this with dsefix then I will bypass the integrity checks and driver should load fine without logging?
AFAIK this method does not work any longer (since W2K3) unless your driver is named win32k.sys and is stored in the same place as the real win32k.sys is. There is an explicit check about that. Maybe, something changed since I looked at this code, however.

I don't know about any sort of a "holy gral" method. You can use **FilterLoad** routine which instruct Filter Manager to load your driver. But this is done via **ZwLoadDriver** internally. The only difference is that the call is made from kernel mode (**FilterLoad** sends an IOCTL to Filter Manager).

Of course, you can take advantage of the following approach:
1) find a bogus driver that e.g. allows you to execute arbitrary code in Ring 0,
2) as a shellcode, prepare your own PE loader,
3) the shellcode loads your driver manually.

However, such an approach may be undesirable in many scenarios I think.
 #27550  by Vrtule
 Tue Jan 05, 2016 1:35 pm
billbudsocket wrote:MmLoadSystemImage can be reached without image name/path restrictions via NtSetSystemInformation/SystemHotpatchInformation. SeDebugPrivilege and SeLoadDriverPrivilege privs required.
Does this call invoke DriverEntry? Or just loads the driver into memory?
 #27558  by kerpow1
 Tue Jan 05, 2016 8:35 pm
I guess so if dependencies are resolved. As this will just map it straight into kernel memory and if your entrypoint can be called it could also easily be patched. I am going to try this approach I think, seems easier than debugging event logging or the usn journal just to erase sc logging..

@Vrtule, do you know of any examples that implement this loading type? I have looked at ZwLoadDriver before but did not realise I can use key information as opposed to it logging events via scm. Seems interesting.
 #27559  by Vrtule
 Tue Jan 05, 2016 11:11 pm
This code manually creates a service key. AFAIK you don't need to create all these values, just some of them are required for drivers. When writing this code, I just did not want to spent time by determining which values are actually required and which are not.

The first argument is a service name for the new driver, the second one an absolute path to driver file (the routine automatically prepends the \??\ string).
Code: Select all
DWORD _InstallDriver(PWCHAR ServiceName, PWCHAR FileName)
{
	DWORD dwordValue = 0;
	HKEY driverKey = NULL;
	HKEY servicesHandle = NULL;
	DWORD ret = ERROR_GEN_FAILURE;

	ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\services", 0, KEY_WRITE, &servicesHandle);
	if (ret == ERROR_SUCCESS) {
		ret = RegCreateKeyEx(servicesHandle, ServiceName, 0, NULL, 0, KEY_WRITE, NULL, &driverKey, NULL);
		if (ret == ERROR_SUCCESS) {
			dwordValue = SERVICE_KERNEL_DRIVER;
			ret = RegSetValueExW(driverKey, L"Type", 0, REG_DWORD, (PBYTE)&dwordValue, sizeof(DWORD));
			if (ret == ERROR_SUCCESS) {
				dwordValue = SERVICE_SYSTEM_START;
				ret = RegSetValueExW(driverKey, L"Start", 0, REG_DWORD, (PBYTE)&dwordValue, sizeof(DWORD));
				if (ret == ERROR_SUCCESS) {
					dwordValue = SERVICE_ERROR_NORMAL;
					ret = RegSetValueExW(driverKey, L"ErrorControl", 0, REG_DWORD, (PBYTE)&dwordValue, sizeof(DWORD));
					if (ret == ERROR_SUCCESS) {
						PWCHAR imagePath = NULL;
						DWORD imagePathLen = (4 + wcslen(FileName))*sizeof(WCHAR);

						imagePath = (PWCHAR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, imagePathLen + sizeof(WCHAR));
						if (imagePath != NULL) {
							SIZE_T prefixLen = wcslen(L"\\??\\")*sizeof(WCHAR);
							memcpy(imagePath, L"\\??\\", prefixLen);
							memcpy(imagePath + (prefixLen / sizeof(WCHAR)), FileName, imagePathLen - prefixLen);
							ret = RegSetValueExW(driverKey, L"ImagePath", 0, REG_SZ, (PBYTE)imagePath, imagePathLen + sizeof(WCHAR));
							HeapFree(GetProcessHeap(), 0, imagePath);
						} else ret = ERROR_NOT_ENOUGH_MEMORY;
					}
				}
			}

			if (ret != ERROR_SUCCESS)
				RegDeleteKeyW(servicesHandle, ServiceName);

			RegCloseKey(driverKey);
		}

		RegCloseKey(servicesHandle);
	}

	return ret;
}
Then, you just need to call NtLoadDriver with a propper parameter.
Code: Select all
NTSTATUS _LoadUnloadDriver(PWCHAR ServiceName, BOOLEAN Load)
{
	PWCHAR driverName = NULL;
	SIZE_T prefixLen = 0;
	SIZE_T serviceLen = 0;
	PWCHAR prefix = L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\services\\";
	typedef NTSTATUS(WINAPI NTLOADDRIVER)(PUNICODE_STRING DriverName);
	typedef NTSTATUS(WINAPI NTUNLOADDRIVER)(PUNICODE_STRING DriverName);
	NTSTATUS status = STATUS_UNSUCCESSFUL;
	UNICODE_STRING uDriverName;

	prefixLen = wcslen(prefix);
	serviceLen = wcslen(ServiceName);
	driverName = (PWCHAR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (prefixLen + serviceLen + 1)*sizeof(WCHAR));
	if (driverName != NULL) {
		memcpy(driverName, prefix, prefixLen*sizeof(WCHAR));
		memcpy(driverName + prefixLen, ServiceName, serviceLen*sizeof(WCHAR));
		uDriverName.Buffer = driverName;
		uDriverName.Length = (USHORT)((prefixLen + serviceLen)*sizeof(WCHAR));
		uDriverName.MaximumLength = uDriverName.Length;
		if (Load) {
			NTLOADDRIVER *_NtLoadDriver = NULL;

			_NtLoadDriver = (NTLOADDRIVER *)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtLoadDriver");
			if (_NtLoadDriver != NULL)
				status = _NtLoadDriver(&uDriverName);
			else status = STATUS_NOT_FOUND;
		} else {
			NTUNLOADDRIVER *_NtUnloadDriver = NULL;

			_NtUnloadDriver = (NTUNLOADDRIVER *)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtUnloadDriver");
			if (_NtUnloadDriver != NULL)
				status = _NtUnloadDriver(&uDriverName);
			else status = STATUS_NOT_FOUND;
		}

		HeapFree(GetProcessHeap(), 0, driverName);
	} else status = STATUS_NO_MEMORY;

	return status;
}
Of course, if you create a registry key for your driver and reboot the computer, SCM will see the key and creates a service for you (the service will be then visible in its local database).

HTH

EDIT:
Statics removed