Hacking RPC Execute Assembly

BullyWiiPlaza

Nintendo Hacking <3
OP
Member
Joined
Aug 2, 2014
Messages
1,932
Trophies
0
XP
2,477
Country
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:
z9d1OvA.jpg

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
 
Joined
Apr 19, 2015
Messages
1,023
Trophies
1
Location
Stuck in the PowerPC
Website
heyquark.com
XP
3,909
Country
Australia
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:
z9d1OvA.jpg

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

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.
 
  • Like
Reactions: BullyWiiPlaza

BullyWiiPlaza

Nintendo Hacking <3
OP
Member
Joined
Aug 2, 2014
Messages
1,932
Trophies
0
XP
2,477
Country
Germany
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.
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.
 

BullyWiiPlaza

Nintendo Hacking <3
OP
Member
Joined
Aug 2, 2014
Messages
1,932
Trophies
0
XP
2,477
Country
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
74f96sc7.png
 
Last edited by BullyWiiPlaza,

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    K3Nv2 @ K3Nv2: they be like which lite firefox exe pls