A forum for reverse engineering, OS internals and malware analysis 

 #31439  by Li Yong
 Thu Apr 12, 2018 7:47 pm
Hello,

I want undestand (with your help) why my IndexOf() routine generate a BSOD. In my tests i noted that is when this routine is executed by second time.

This code was based in this answer and have the goal of find all processes that is using determinated file, but in my case i need that not repeat the same process name because i have another routine that not must be executed when is a repeated process (this is the reason to this ArrayList implementation).

This question is similar to this http://www.kernelmode.info/forum/viewto ... =15&t=4970 that was solved using TStringList (Delphi).
Code: Select all
#pragma region ArrayList

typedef unsigned char uint8_t;

typedef struct
{
	UNICODE_STRING *data;
}Element;

typedef struct
{
	int current;
	int size;
	int increment_rate;
	Element *elements;
}ArrayList;

void FreeUString(UNICODE_STRING *src)
{
	RtlFreeUnicodeString(src);
	src->Length = src->MaximumLength = 0;
}

void initWithSizeAndIncRate(ArrayList *const list, int size, int rate)
{
	list->size = size;
	list->increment_rate = rate;
	list->elements = (Element*)ExAllocatePoolWithTag(NonPagedPool, sizeof(Element), 'Foo');
	list->current = -1;
}

void initWithSize(ArrayList *const list, int size)
{
	initWithSizeAndIncRate(list, size, 50);
}

void init(ArrayList *const list)
{
	initWithSize(list, 100);
}

void arraryCopy(void *dest, int dIndex, const void* src, int sIndex, int len, int destLen, size_t size)
{
	uint8_t *udest = (uint8_t*)dest;
	uint8_t *usrc = (uint8_t*)src;
	dIndex *= size;
	sIndex *= size;
	len *= size;
	destLen *= size;

	if (src != dest)
	{
		memcpy(&udest[dIndex], &usrc[sIndex], len);
	}
	else
	{
		if (dIndex > sIndex)
		{
			uint8_t *tmp = (uint8_t*)ExAllocatePoolWithTag(NonPagedPool, size, 'Foo');
			memcpy(tmp, &udest[dIndex], (destLen - dIndex));
			memcpy(&udest[dIndex], &usrc[sIndex], len);
			memcpy(&udest[dIndex + len], tmp, (destLen - dIndex));
			ExFreePoolWithTag(tmp, 'Foo');
		}
		else if (sIndex > dIndex)
		{
			memcpy(&udest[dIndex], &usrc[sIndex], (destLen - sIndex) + 1);
		}
		else
			return;
	}
}

void clear(ArrayList *const list)
{
	while (list->current >= 0)
	{
		FreeUString(list->elements[list->current].data);
		list->current--;
	}
}

void wide(ArrayList* const list)
{
	list->size += list->increment_rate;
	Element *newArr = (Element*)ExAllocatePoolWithTag(NonPagedPool, sizeof(Element), 'Foo');
	arraryCopy(newArr, 0, list->elements, 0, list->current, list->size, sizeof(Element));
	//ExFreePoolWithTag(list->elements, 'Foo');
	list->elements = newArr;
}

int add(ArrayList *const list, Element *e)
{
	UNICODE_STRING *dest = { NULL };

	if (++list->current < list->size)
	{
		RtlDuplicateUnicodeString(1, e->data, dest);
		list->elements[list->current].data = dest;
		return 1;
	}
	else
	{
		wide(list);
		RtlDuplicateUnicodeString(1, e->data, dest);
		list->elements[list->current].data = dest;
		return 1;
	}
	return 0;
}

int indexOf(const ArrayList *const list, Element *e)
{
	int index = 0;
	while (index <= list->current)
	{
		if (e->data->Length == list->elements[index].data->Length &&
			0 == wcsncmp(e->data->Buffer,
				list->elements[index].data->Buffer,
				list->elements[index].data->Length))
			return index;
		index++;
	}
	return 0;
}

void clean(ArrayList *list)
{
	ExFreePoolWithTag(list->elements, 'Foo');
}

ArrayList list;
Element e;

#pragma endregion ArrayList

typedef struct SYSTEM_PROCESS_INFORMATION {
	ULONG NextEntryOffset;
	ULONG NumberOfThreads;
	LARGE_INTEGER Reserved[3];
	LARGE_INTEGER CreateTime;
	LARGE_INTEGER UserTime;
	LARGE_INTEGER KernelTime;
	UNICODE_STRING ImageName;
	DWORD BasePriority;
	HANDLE ProcessId;
	HANDLE ParentProcessId;
	ULONG HandleCount;
	ULONG Reserved2[2];
	VM_COUNTERS VMCounters;
	IO_COUNTERS IOCounters;
	SYSTEM_THREAD Threads[1];
}  SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

NTSTATUS PrintProcessesUsingFile(PFILE_PROCESS_IDS_USING_FILE_INFORMATION ppiufi)
{

do
	{
		data.pb += NextEntryOffset;

		ULONG NumberOfProcessIdsInList = ppiufi->NumberOfProcessIdsInList;

		PULONG_PTR ProcessIdList = ppiufi->ProcessIdList;
		do
		{
			if (*ProcessIdList++ == (ULONG_PTR)data.pspi->ProcessId)
			{
				DbgPrint("%d %wZ\n", data.pspi->ProcessId, &data.pspi->ImageName);

				e.data = &(data.pspi->ImageName);

			  ///////////////////////////////
				
				int i = indexOf(&list, &e);
				
			  //////////////////////////////

				if (i > 0)
				{
					DbgPrint("process already in list \n");
				}
				else
				{
					add(&list, &e);
					clean(&list);
					// My another routine here when is a different process name
				}
				break;
			}
		} while (--NumberOfProcessIdsInList);

	} while (NextEntryOffset = data.pspi->NextEntryOffset);
}

NTSTATUS testPnP(IN PDEVICE_OBJECT pDeviceObject, IN PIRP Irp)
{
	PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);

	switch (irpSp->Parameters.DeviceIoControl.IoControlCode)
	{

	case IOCTL_REQUEST:

		init(&list);

		Irp->IoStatus.Status = STATUS_SUCCESS;
		Irp->IoStatus.Information = 0;
		IoCompleteRequest(Irp, IO_NO_INCREMENT);

		return STATUS_SUCCESS;
	}
}
Here is result of !analyze -v (on .dump file) from WinDbg x86:
Code: Select all
FAULTING_SOURCE_LINE_NUMBER:  259

FAULTING_SOURCE_CODE:  
   255: {
   256: 	int index = 0;
   257: 	while (index <= list->current)
   258: 	{
>  259: 		if (e->data->Length == list->elements[index].data->Length &&
   260: 			0 == wcsncmp(e->data->Buffer,
   261: 				list->elements[index].data->Buffer,
   262: 				list->elements[index].data->Length))
   263: 			return index;
   264: 		index++;
 #31441  by Li Yong
 Fri Apr 13, 2018 11:25 am
EP_X0FF wrote: Fri Apr 13, 2018 3:38 am Have you really read and understand my previous reply?

All your code is broken and unworkable by design.
I not wanted insert a big quantity of code here (to prevent noise), but the first link above have the complete routines (missing only some structures to complement SYSTEM_PROCESS_INFORMATION like):

VM_COUNTERS, IO_COUNTERS and SYSTEM_THREAD
 #31443  by Li Yong
 Sat Apr 14, 2018 1:34 am
I compiled a minimal example that enumerates all running process to better understand. The following code compiles fine on VS 2017 + WDK (latest version).
Code: Select all
#include <ntddk.h>
#include <WinDef.h>

#define NTOSAPI __declspec(dllimport)

NTSTATUS NTAPI RtlDuplicateUnicodeString(IN ULONG Flags, IN PCUNICODE_STRING SourceString, OUT PUNICODE_STRING DestinationString);

///////////////////////////////////// START ARRAYLIST /////////////////////////////////////////

typedef unsigned char uint8_t;

typedef struct
{
	UNICODE_STRING *data;
}Element;

typedef struct
{
	int current;
	int size;
	int increment_rate;
	Element *elements;
}ArrayList;

void FreeUString(UNICODE_STRING *src)
{
	RtlFreeUnicodeString(src);
	src->Length = src->MaximumLength = 0;
}

void initWithSizeAndIncRate(ArrayList *const list, int size, int rate)
{
	list->size = size;
	list->increment_rate = rate;
	list->elements = (Element*)ExAllocatePoolWithTag(NonPagedPool, sizeof(Element), 'Fo');
	list->current = -1;
}

void initWithSize(ArrayList *const list, int size)
{
	initWithSizeAndIncRate(list, size, 50);
}

void init(ArrayList *const list)
{
	initWithSize(list, 100);
}

void arraryCopy(void *dest, int dIndex, const void* src, int sIndex, int len, int destLen, size_t size)
{
	uint8_t *udest = (uint8_t*)dest;
	uint8_t *usrc = (uint8_t*)src;
	dIndex *= size;
	sIndex *= size;
	len *= size;
	destLen *= size;

	if (src != dest)
	{
		memcpy(&udest[dIndex], &usrc[sIndex], len);
	}
	else
	{
		if (dIndex > sIndex)
		{
			uint8_t *tmp = (uint8_t*)ExAllocatePoolWithTag(NonPagedPool, size, 'F');
			memcpy(tmp, &udest[dIndex], (destLen - dIndex));
			memcpy(&udest[dIndex], &usrc[sIndex], len);
			memcpy(&udest[dIndex + len], tmp, (destLen - dIndex));
			ExFreePoolWithTag(tmp, 'F');
		}
		else if (sIndex > dIndex)
		{
			memcpy(&udest[dIndex], &usrc[sIndex], (destLen - sIndex) + 1);
		}
		else
			return;
	}
}

void clear(ArrayList *const list)
{
	while (list->current >= 0)
	{
		FreeUString(list->elements[list->current].data);
		list->current--;
	}
}

void wide(ArrayList* const list)
{
	list->size += list->increment_rate;
	Element *newArr = (Element*)ExAllocatePoolWithTag(NonPagedPool, sizeof(Element), 'T');
	arraryCopy(newArr, 0, list->elements, 0, list->current, list->size, sizeof(Element));
	//ExFreePoolWithTag(list->elements, 'Foo');
	list->elements = newArr;
}

int add(ArrayList *const list, Element *e)
{
	UNICODE_STRING *dest = { NULL };
	NTSTATUS status = STATUS_SUCCESS;

	if (++list->current < list->size)
	{
		status = RtlDuplicateUnicodeString(1, e->data, dest);
		DbgPrint("RtlDuplicateUnicodeString() status: 0x%x", status);

		list->elements[list->current].data = dest;
		return 1;
	}
	else
	{
		wide(list);
		status = RtlDuplicateUnicodeString(1, e->data, dest);
		DbgPrint("RtlDuplicateUnicodeString() status: 0x%x", status);
		list->elements[list->current].data = dest;
		return 1;
	}
	return 0;
}

int indexOf(const ArrayList *const list, Element *e)
{
	int index = 0;
	while (index <= list->current)
	{
		if (e->data->Length == list->elements[index].data->Length &&
			0 == wcsncmp(e->data->Buffer,
				list->elements[index].data->Buffer,
				list->elements[index].data->Length))
			return index;
		index++;
	}
	return 0;
}

void clean(ArrayList *list)
{
	ExFreePoolWithTag(list->elements, 'Fo');
}

ArrayList list;
Element e;

///////////////////////////////// END ARRAYLIST ///////////////////////////////////

typedef enum _THREAD_STATE {
	StateInitialized,
	StateReady,
	StateRunning,
	StateStandby,
	StateTerminated,
	StateWait,
	StateTransition,
	StateUnknown
} THREAD_STATE;

typedef struct _SYSTEM_THREAD {
	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;
	LONG           WaitReason;
} SYSTEM_THREAD, *PSYSTEM_THREAD;

typedef struct SYSTEM_PROCESS_INFORMATION {
	ULONG NextEntryOffset;
	ULONG NumberOfThreads;
	LARGE_INTEGER Reserved[3];
	LARGE_INTEGER CreateTime;
	LARGE_INTEGER UserTime;
	LARGE_INTEGER KernelTime;
	UNICODE_STRING ImageName;
	DWORD BasePriority;
	HANDLE ProcessId;
	HANDLE ParentProcessId;
	ULONG HandleCount;
	ULONG Reserved2[2];
	VM_COUNTERS VMCounters;
	IO_COUNTERS IOCounters;
	SYSTEM_THREAD Threads[1];
}  SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

typedef enum _SYSTEM_INFORMATION_CLASS
{
	SystemBasicInformation = 0,
	SystemProcessorInformation = 1,
	SystemPerformanceInformation = 2,
	SystemTimeOfDayInformation = 3,
	SystemPathInformation = 4,
	SystemProcessInformation = 5,
	SystemCallCountInformation = 6,
	SystemDeviceInformation = 7,
	SystemProcessorPerformanceInformation = 8,
	SystemFlagsInformation = 9,
	SystemCallTimeInformation = 10,
	SystemModuleInformation = 11,
	SystemLocksInformation = 12,
	SystemStackTraceInformation = 13,
	SystemPagedPoolInformation = 14,
	SystemNonPagedPoolInformation = 15,
	SystemHandleInformation = 16,
	SystemObjectInformation = 17,
	SystemPageFileInformation = 18,
	SystemVdmInstemulInformation = 19,
	SystemVdmBopInformation = 20,
	SystemFileCacheInformation = 21,
	SystemPoolTagInformation = 22,
	SystemInterruptInformation = 23,
	SystemDpcBehaviorInformation = 24,
	SystemFullMemoryInformation = 25,
	SystemLoadGdiDriverInformation = 26,
	SystemUnloadGdiDriverInformation = 27,
	SystemTimeAdjustmentInformation = 28,
	SystemSummaryMemoryInformation = 29,
	SystemMirrorMemoryInformation = 30,
	SystemPerformanceTraceInformation = 31,
	SystemObsolete0 = 32,
	SystemExceptionInformation = 33,
	SystemCrashDumpStateInformation = 34,
	SystemKernelDebuggerInformation = 35,
	SystemContextSwitchInformation = 36,
	SystemRegistryQuotaInformation = 37,
	SystemExtendServiceTableInformation = 38,
	SystemPrioritySeperation = 39,
	SystemVerifierAddDriverInformation = 40,
	SystemVerifierRemoveDriverInformation = 41,
	SystemProcessorIdleInformation = 42,
	SystemLegacyDriverInformation = 43,
	SystemCurrentTimeZoneInformation = 44,
	SystemLookasideInformation = 45,
	SystemTimeSlipNotification = 46,
	SystemSessionCreate = 47,
	SystemSessionDetach = 48,
	SystemSessionInformation = 49,
	SystemRangeStartInformation = 50,
	SystemVerifierInformation = 51,
	SystemVerifierThunkExtend = 52,
	SystemSessionProcessInformation = 53,
	SystemLoadGdiDriverInSystemSpace = 54,
	SystemNumaProcessorMap = 55,
	SystemPrefetcherInformation = 56,
	SystemExtendedProcessInformation = 57,
	SystemRecommendedSharedDataAlignment = 58,
	SystemComPlusPackage = 59,
	SystemNumaAvailableMemory = 60,
	SystemProcessorPowerInformation = 61,
	SystemEmulationBasicInformation = 62,
	SystemEmulationProcessorInformation = 63,
	SystemExtendedHandleInformation = 64,
	SystemLostDelayedWriteInformation = 65,
	SystemBigPoolInformation = 66,
	SystemSessionPoolTagInformation = 67,
	SystemSessionMappedViewInformation = 68,
	SystemHotpatchInformation = 69,
	SystemObjectSecurityMode = 70,
	SystemWatchdogTimerHandler = 71,
	SystemWatchdogTimerInformation = 72,
	SystemLogicalProcessorInformation = 73,
	SystemWow64SharedInformation = 74,
	SystemRegisterFirmwareTableInformationHandler = 75,
	SystemFirmwareTableInformation = 76,
	SystemModuleInformationEx = 77,
	SystemVerifierTriageInformation = 78,
	SystemSuperfetchInformation = 79,
	SystemMemoryListInformation = 80,
	SystemFileCacheInformationEx = 81,
	MaxSystemInfoClass = 82

} SYSTEM_INFORMATION_CLASS;

NTOSAPI NTSTATUS NTAPI ZwQuerySystemInformation(/*IN*/ SYSTEM_INFORMATION_CLASS SystemInformationClass, /*IN OUT*/ PVOID SystemInformation, /*IN*/ ULONG SystemInformationLength, /*OUT*/ PULONG ReturnLength OPTIONAL);

NTSTATUS EnumProcesses()
{
	NTSTATUS status;

	ULONG cb = 0x8000;

	do
	{
		status = STATUS_INSUFFICIENT_RESOURCES;
		PVOID buf = ExAllocatePoolWithTag(PagedPool, cb, 'tset');

		if (buf)
		{
			if (0 <= (status = ZwQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
			{
				union Data {
					PVOID pv;
					PBYTE pb;
					PSYSTEM_PROCESS_INFORMATION pspi;
				};

				union Data data;

				data.pv = buf;
				ULONG NextEntryOffset = 0;

				do
				{
					data.pb += NextEntryOffset;


					DbgPrint("%d %wZ\n", data.pspi->ProcessId, &data.pspi->ImageName);

					e.data = &data.pspi->ImageName;

					int i = indexOf(&list, &e);

					if (i > 0)
					{
						DbgPrint("process already in list \n");
					}
					else
					{
						add(&list, &e);
					}

				} while (NextEntryOffset = data.pspi->NextEntryOffset);
			}

			ExFreePoolWithTag(buf, 'tset');
		}

	} while (status == STATUS_INFO_LENGTH_MISMATCH);
	clean(&list);
	return status;
}

void EnumProcUnload(IN PDRIVER_OBJECT DriverObject)
{
	DbgPrint("Goodbye from teste!\n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING  RegistryPath)
{

	init(&list);
	EnumProcesses();

	DriverObject->DriverUnload = EnumProcUnload;

	DbgPrint("Hello from teste!\n");

	return STATUS_SUCCESS;
}
BSOD:
Code: Select all
FAULTING_SOURCE_LINE_NUMBER:  128

FAULTING_SOURCE_CODE:  
   124: {
   125: 	int index = 0;
   126: 	while (index <= list->current)
   127: 	{
>  128: 		if (e->data->Length == list->elements[index].data->Length &&
   129: 			0 == wcsncmp(e->data->Buffer,
   130: 				list->elements[index].data->Buffer,
   131: 				list->elements[index].data->Length))
   132: 			return index;
   133: 		index++;
 #31444  by Vrtule
 Sat Apr 14, 2018 9:28 am
Well, you went through the code a little bit and see some problems (but there will be probably more of them):

1) when initializing the array, you always allocate space only for one element, altough the size (capacity) of the array is set to 100,

2) in the array, you are storing pointers to UNICODE_STRINGs, not the structures themselves. So, you must be careful not to free these unicode strings when their addresses remain in the array. The same applies to their content. Remember tha pointers (and the problem of their ownership) are nasty beasts and you may get into a real hell without utilizing practices like reference counting. The safest we is to copy the whole UNICODE_STRINGs (i.e. make a deep copies) into your array. Yes, it degrates performance and consumes more memory but you avoid many problems. I don't consider the performance and memory consumption as problems you need to cope with right now.

Or just start with your list large enough to satisfy your needs without its reallocations. You may add the reallocation later.

Really, you should be familiar with pointers and with design of your own data structures before attempting to write drivers. These concepts can be learnt and understood purely from usermode. Don't be offended by this remark, it's not meant that way.
 #31445  by Li Yong
 Sat Apr 14, 2018 11:33 am
Thank you by answer Vrtule, you is right about size of array on initialization.

I tried pass this following implementation (usermode) to kernel mode replacing malloc, calloc, free and CopyUString() ( to RtlDuplicateUnicodeString) to your correspondent in kernel mode (already that these routines not is avaiable to kernel drivers) but without success based in your answer.
Code: Select all
typedef struct
{
	UNICODE_STRING *data;
}Element;

typedef struct
{
	int current;
	int size;
	int increment_rate;
	Element *elements;
}ArrayList;

UNICODE_STRING * CopyUString(UNICODE_STRING *src)
{
	UNICODE_STRING *dest = (UNICODE_STRING *)malloc(sizeof(UNICODE_STRING));
	dest->Length = src->Length;
	dest->MaximumLength = src->MaximumLength;
	dest->Buffer = (PWSTR)malloc(sizeof(WCHAR) * dest->MaximumLength);
	memcpy(dest->Buffer, src->Buffer, sizeof(WCHAR) * dest->MaximumLength);
	return dest;
}

void FreeUString(UNICODE_STRING *src)
{
	free(src->Buffer);
	src->Length = src->MaximumLength = 0;
}

void initWithSizeAndIncRate(ArrayList *const list, int size, int rate)
{
	list->size = size;
	list->increment_rate = rate;
	list->elements = (Element*)calloc(sizeof(Element), list->size);
	list->current = -1;
}

void initWithSize(ArrayList *const list, int size)
{
	initWithSizeAndIncRate(list, size, 50);
}

void init(ArrayList *const list)
{
	initWithSize(list, 100);
}

void arraryCopy(void *dest, int dIndex, const void* src, int sIndex, int len, int destLen, size_t size)
{
	uint8_t *udest = (uint8_t*)dest;
	uint8_t *usrc = (uint8_t*)src;
	dIndex *= size;
	sIndex *= size;
	len *= size;
	destLen *= size;

	if (src != dest)
	{
		memcpy(&udest[dIndex], &usrc[sIndex], len);
	}
	else
	{
		if (dIndex > sIndex)
		{
			uint8_t *tmp = (uint8_t*)calloc(destLen, size);
			memcpy(tmp, &udest[dIndex], (destLen - dIndex));
			memcpy(&udest[dIndex], &usrc[sIndex], len);
			memcpy(&udest[dIndex + len], tmp, (destLen - dIndex));
			free(tmp);
		}
		else if (sIndex > dIndex)
		{
			memcpy(&udest[dIndex], &usrc[sIndex], (destLen - sIndex) + 1);
		}
		else
			return;
	}
}

void clear(ArrayList *const list)
{
	while (list->current >= 0)
	{
		FreeUString(list->elements[list->current].data);
		list->current--;
	}
}

void wide(ArrayList* const list)
{
	list->size += list->increment_rate;
	Element *newArr = (Element*)calloc(sizeof(Element), list->size);
	arraryCopy(newArr, 0, list->elements, 0, list->current, list->size, sizeof(Element));
	free(list->elements);
	list->elements = newArr;
}

int add(ArrayList *const list, Element *e)
{
	if (++list->current < list->size)
	{
		list->elements[list->current].data = CopyUString(e->data);
		return 1;
	}
	else
	{
		wide(list);
		list->elements[list->current].data = CopyUString(e->data);
		return 1;
	}
	return 0;
}

int indexOf(const ArrayList *const list, Element *e)
{
	int index = 0;
	while (index <= list->current)
	{
		if (e->data->Length == list->elements[index].data->Length &&
			0 == wcsncmp(e->data->Buffer,
			list->elements[index].data->Buffer,
			list->elements[index].data->Length))
			return index;
		index++;
	}
	return 0;
}

void clean(ArrayList *list)
{
	free(list->elements);
}
Then what value i can define in ExeAllocatePoolWithTag to allocate the correct size on array? arraryCopy(), initWithSizeAndIncRate() and wide() routines respectivally for example?