RPC Execute Assembly

Discussion in 'Wii U - Hacking & Backup Loaders' started by BullyWiiPlaza, Mar 21, 2017.

  1. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,729
    1,409
    Aug 2, 2014
    Germany
    For executing assembly in the form of a char array I wrote the following code:
    Code:
    #define ASSEMBLY_BUFFER_SIZE 0x190
    Code:
    void receiveString(struct pygecko_bss_t *bss,
                       int clientfd,
                       char *stringBuffer,
                       int bufferSize) {
        // Receive the string length
        char lengthBuffer[4] = {0};
        int ret = recvwait(bss, clientfd, lengthBuffer, 4);
        ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (string length)")
        int stringLength = ((int *) lengthBuffer)[0];
    
        if (stringLength >= 0 && stringLength <= bufferSize) {
            // Receive the actual string
            ret = recvwait(bss, clientfd, stringBuffer, stringLength);
            ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (string)")
        } else {
            OSFatal("String buffer size exceeded");
        }
    }
    Code:
    case COMMAND_EXECUTE_ASSEMBLY: {
        char assemblyBuffer[ASSEMBLY_BUFFER_SIZE] = {0};
    
        // Receive the assembly
        receiveString(bss, clientfd, assemblyBuffer, ASSEMBLY_BUFFER_SIZE);
    
        // Execute the assembly
        void (*function)() = (void *) assemblyBuffer;
        function();
    
        break;
    }
    The client sends the length of the assembly bytes and the assembly bytes themselves:
    Code:
    public void executeAssembly(byte[] assembly) throws Exception
    {
        int assemblyLength = assembly.length;
        if (assemblyLength <= 0 || assemblyLength % 4 != 0)
        {
            throw new IllegalStateException("Bad assembly bytes length: " + assemblyLength);
        }
    
        assembly = Assembler.forceReturnStatement(assembly);
    
        try (CloseableReentrantLock ignored = reentrantLock.acquire())
        {
            sendCommand(Command.EXECUTE_ASSEMBLY);
            dataSender.writeInt(assembly.length);
            dataSender.write(assembly);
            dataSender.flush();
        }
    }
    When I use this e.g. like with the following assembly:
    Code:
    byte[] bytes = Assembler.assembleBytes("lis r12, 0x1200\n" +
            "ori r12, r12, 0x0000\n" +
            "li r11, 0x1337\n" +
            "stw r11, 0 (r12)");
    executeAssembly(bytes);
    The console crashes with an Exception ISI:
    Warning: Spoilers inside!

    The code dump shows the assembly I gave it to execute and how a blr instruction is added at the end so the code returns to the caller. Furthermore, the link register LR contains an invalid address which might have been the reason for the crash.

    Why is this happening and how to fix it? I guess the intention is clear: I want to send over some bytes and have them execute via the TCP Gecko Installer thread and continue its execution right after.

    @NWPlayer123
    @QuarkTheAwesome
    @FIX94
    @dimok
    @Maschell
    @CosmoCortney
     
  2. QuarkTheAwesome

    QuarkTheAwesome Working for Hugs

    Member
    788
    1,929
    Apr 19, 2015
    Australia
    Stuck in the PowerPC
    I'm pretty sure MEM2 isn't executable by default. You'll either need to read the data into somewhere executable (HBL area?) or make MEM2 executable with a BAT.
    The crash is because the PowerPC has separate memory mappings for data and instructions. If an area is mapped for data but not instructions it essentially becomes non-executable, since trying to execute it is trying to run instructions at an unmapped address.
     
    BullyWiiPlaza likes this.
  3. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,729
    1,409
    Aug 2, 2014
    Germany
    Alright, what would be a good solution code-wise? mmap() does not exist so I'm not sure how to make it executable in a generic fashion.
     
  4. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,729
    1,409
    Aug 2, 2014
    Germany
    Nevermind. I got it to work. The trick was like @QuarkTheAwesome said, to write the assembly to an executable data section and then executing it from there:
    Code:
    case COMMAND_EXECUTE_ASSEMBLY: {
        // Receive the assembly
        receiveString(bss, clientfd, (char *) buffer, DATA_BUFFER_SIZE);
    
        // Write the assembly to an executable code region
        int destinationAddress = 0x10000000 - DATA_BUFFER_SIZE;
        pygecko_memcpy((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE);
    
        // Execute the assembly from there
        void (*function)() = (void (*)()) destinationAddress;
        function();
    
        // Clear the memory contents again
        memset((void *) buffer, 0, DATA_BUFFER_SIZE);
        pygecko_memcpy((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE);
    
        break;
    }
    L33t h4x0r way of poking a value :D
    [​IMG]
     
    Last edited by BullyWiiPlaza, Mar 29, 2017
  5. PandaOnSmack

    PandaOnSmack GBAtemp Fan

    Member
    323
    183
    Nov 3, 2015
    :)
     
    BullyWiiPlaza likes this.