RPC Range Disassembler Help

Discussion in 'Wii U - Homebrew' started by BullyWiiPlaza, Oct 11, 2016.

  1. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,693
    1,377
    Aug 2, 2014
    Germany
    I would like to call the DisassemblePPCRange() function via RPC. You can see the documentation by of it here. Disassembling so far requires platform dependent binaries to be possible in TCP Gecko clients so it is a pain to support Windows, Linux and Mac OS X. Getting DisassemblePPCRange() to work would make the powerpc-eabi binaries redundant since we can let the Wii U disassemble everything for us :D

    So far I "almost" got DisassemblePPCRange() working but I'm not sure about how exactly to call it. I got the DisassemblePPCOpcode() function working already but it only does one instruction at a time which is way too slow to apply to an entire range:
    Code:
    public static String disassembleValue(int value) throws IOException
    {
        try (AllocatedMemory allocatedMemory = new AllocatedMemory(4, 4))
        {
            int address = allocatedMemory.getAddress();
    
            // Write the value into the allocated memory address
            MemoryWriter memoryWriter = new MemoryWriter();
            memoryWriter.writeInt(address, value);
    
            // Disassemble and return the result
            return disassembleAddress(address);
        }
    }
    
    public static String disassembleAddress(int address) throws IOException
    {
        int instructionBufferLength = 0x40;
    
        try (FindSymbol findSymbol = new FindSymbol();
            AllocatedMemory instructionBuffer = new AllocatedMemory(instructionBufferLength, 0x20))
        {
            RemoteProcedureCall remoteProcedureCall = new RemoteProcedureCall();
            ExportedSymbol exportedSymbol = remoteProcedureCall.getSymbol("coreinit.rpl", "DisassemblePPCOpcode");
            remoteProcedureCall.call(exportedSymbol,
                    address,
                    instructionBuffer.getAddress(),
                    instructionBufferLength,
                    findSymbol.getAddress(),
                    0);
    
            // Read the output buffer's contents
            MemoryReader memoryReader = new MemoryReader();
            byte[] disassembledBytes = memoryReader.readBytes(instructionBuffer.getAddress(), instructionBufferLength);
    
            // Clean up the disassembled String
            String disassembled = new String(disassembledBytes);
            disassembled = disassembled.replaceAll("\\(null\\)", " ");
            disassembled = disassembled.replaceAll("\\s+", " ");
            disassembled = disassembled.trim();
    
            return disassembled;
        }
    }
    Now the non-functional RPC implementation of DisassemblePPCRange():
    Code:
    // TODO Non-functional yet
    public static String disassembleRange(int start, int length) throws IOException
    {
        int end = start + length;
    
        try (FindSymbol findSymbol = new FindSymbol();
             RemoteString printingFunction = new RemoteString("%s"))
        {
            RemoteProcedureCall remoteProcedureCall = new RemoteProcedureCall();
            ExportedSymbol exportedSymbol = remoteProcedureCall.getSymbol("coreinit.rpl", "DisassemblePPCRange");
            remoteProcedureCall.call(exportedSymbol, start, end,
                    printingFunction.getAddress(), findSymbol.getAddress(), 0); // Crashes here
    
            MemoryReader memoryReader = new MemoryReader();
            byte[] disassembledBytes = memoryReader.readBytes(printingFunction.getAddress(), length);
            String disassembled = new String(disassembledBytes);
    
            disassembledBytes = memoryReader.readBytes(findSymbol.getAddress(), length);
            String disassembled2 = new String(disassembledBytes);
    
            return disassembled;
        }
    }
    Any help or tips on how to fix the above code would be appreciated. If you can do the same in PyGecko then let me know because that's good enough for me.

    @QuarkTheAwesome
     
    CosmoCortney likes this.
  2. QuarkTheAwesome

    QuarkTheAwesome Working for Hugs

    Member
    765
    1,867
    Apr 19, 2015
    Australia
    Stuck in the PowerPC
    While I'm not 100% on exactly how this system is used (I just adapted libwiiu code to work properly ;D) I've got a pretty solid idea of what's going on here.

    If you take a look at my on-console exception handler you'll see that how I use DisassemblePPCRange is essentially the same as how you're calling it (albeit without findSymbol). The major difference I see is exception_disassembly_helper. I don't know how your RemoteString class works but you can see how the function wants it; sorta like printf. That would be my suspicion as to the crash reason.
    Other than that DisassemblePPCRange is very resilient - you can even try an invalid memory range and it just won't output anything.
    (By the way, "fmt" contains the same format string I use; just without the "> " at the front).
     
    Last edited by QuarkTheAwesome, Oct 11, 2016
    CosmoCortney and BullyWiiPlaza like this.
  3. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,693
    1,377
    Aug 2, 2014
    Germany
    Yeah, it's just an allocated string in the memory so it's not function. If it wants a reference to a printing function instead, which one would I use? Just getting the symbole of __os_snprintf()? Seems reasonable.
     
  4. QuarkTheAwesome

    QuarkTheAwesome Working for Hugs

    Member
    765
    1,867
    Apr 19, 2015
    Australia
    Stuck in the PowerPC
    You'd have to fiddle with it a bit - you can see the arguments that get passed in to the helper function and the notably missing ones are the buffer and buffer length. In order to use __os_snprintf you'd have to find a way of getting these in there. Your best bet is to just write some PowerPC somewhere executable to act as your helper function.
     
    BullyWiiPlaza likes this.
  5. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,693
    1,377
    Aug 2, 2014
    Germany
    Uhmm, I guess I will implement the disassembly in C and add another command to the TCP Gecko server so it can do the work :P
     
    QuarkTheAwesome likes this.
  6. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,693
    1,377
    Aug 2, 2014
    Germany
    It's still crashing :(
    Code:
    static void exception_disassembly_helper(char *fmt, int addr, int opcode, char* s)
    {
        char* *store = (char**) 0x1ab5d140;
        char *buffer = (char *) store[0];
    
        if (addr == ((int*) store)[1])
        {
            store[0] += __os_snprintf(buffer, 512, "> 0x%08X    0x%08X    %s\n", addr, opcode, s);
        } else
        {
            store[0] += __os_snprintf(buffer, 512, "  0x%08X    0x%08X    %s\n", addr, opcode, s);
        }
    }
    Code:
    case 0x05: { /* cmd_disassemble_range */
                // Expect 8 bytes
                ret = recvwait(bss, clientfd, buffer, 8);
                CHECK_ERROR(ret < 0);
    
                // Retrieve the start and end addresses
                int *start = ((int **) buffer)[0];
                int *end = ((int **) buffer)[1];
    
                // Disassemble the memory range
                DisassemblePPCRange((void *) start, (void *) end, (void *) exception_disassembly_helper, 0, 0);
             
                // DCFlushRange(start, length);
                break;
            }
    Client code:
    Code:
    sendCommand(Commands.MEMORY_DISASSEMBLE);
    dataSender.writeInt(address);
    dataSender.writeInt(address + length);
    dataSender.flush();
    
    Repository link:
    https://github.com/BullyWiiPlaza/tcpgecko/

    @QuarkTheAwesome
     
    Last edited by BullyWiiPlaza, Oct 12, 2016
  7. QuarkTheAwesome

    QuarkTheAwesome Working for Hugs

    Member
    765
    1,867
    Apr 19, 2015
    Australia
    Stuck in the PowerPC
    The exception handler leaves a few variables for itself, most notably a buffer, at a location in memory.
    Code:
    char buf2[512];
    int* store = (int*)0x1AB5D140;
    store[0] = (int)buf2;
    store[1] = (int)context[38];
    
    This sort of hacky workarounding isn't really needed for code that hasn't crashed already; so a quick refactor of the helper should work out just fine.
    Assuming you can use C global variables (HBL, wut):
    Code:
    char buf[1024]; //maybe make this longer
    static void exception_disassembly_helper(char *fmt, int addr, int opcode, char* s) {
         buf += __os_snprintf(buf, 512, " 0x%08X 0x%08X %s\n", addr, opcode, s);
    }
    
    Assuming you can't use C global variables (browserhax):
    Code:
    static void exception_disassembly_helper(char *fmt, int addr, int opcode, char* s) {
         char* *store = (char**)0x1AB5D140;
         char *buffer = (char *)store[0];
         store[0] += __os_snprintf(buffer, 512, " 0x%08X 0x%08X %s\n", addr, opcode, s);
    }
    
    //somewhere later on...
    case 0x05: { /* cmd_disassemble_range */
         // Expect 8 bytes
         ret = recvwait(bss, clientfd, buffer, 8);
         CHECK_ERROR(ret < 0);
    
         // Retrieve the start and end addresses
         int *start = ((int **) buffer)[0];
         int *end = ((int **) buffer)[1];
    
         char buf2[1024]; //again, this could be longer
         int* store = (int*)0x1AB5D140;
         store[0] = (int)buf2; 
    
         // Disassemble the memory range
         DisassemblePPCRange((void *) start, (void *) end, (void *) exception_disassembly_helper, 0, 0);
    
         // DCFlushRange(start, length);
         break;
    }
    
    Remember that this code was designed as an exception handler so it's probably going to overwrite something vaugely important. If you're getting troubles with the browserhax version I'd change the address of "store" - just remember to change both times it's used!
     
    BullyWiiPlaza likes this.
  8. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,693
    1,377
    Aug 2, 2014
    Germany
    I'm doing the Homebrew Launcher version now because it works for me and it's more up-to-date. Either way, thank you for helping yet again. I will let you know more when I can try this again. Fine tuning the address or format string can be done once it works at all. Maybe allocating the space with an OSAlloc function would be best :P
     
  9. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,693
    1,377
    Aug 2, 2014
    Germany
    I'm now implementing this in C using the official documentation (*cough*) but it's not returning proper results. @QuarkTheAwesome your code doesn't seem to work and crashes. Also, it doesn't use proper data types. The signature of the exception_disassembly_helper() function for instance doesn't match with what it should be:
    Code:
    char *format, ...
    The 2nd function should be OSGetSymbolName() aswell I believe.

    Here is my RPC command implementation:
    Code:
    case 0x07: { /* cmd_memory_disassemble */
        // Receive the starting, ending address and the disassembler options
        ret = recvwait(bss, clientfd, buffer, 4 + 4 + 4);
        CHECK_ERROR(ret < 0)
        void *startingAddress = ((void **) buffer)[0];
        void *endingAddress = ((void **) buffer)[1];
        int disassemblerOptions = ((int *) buffer)[2];
    
        // Disassemble
        DisassemblePPCRange(startingAddress, endingAddress, formatDisassembled, OSGetSymbolName,
                            (u32) disassemblerOptions);
    
        // Send the disassembler buffer size
        int length = DISASSEMBLER_BUFFER_SIZE;
        ret = sendwait(bss, clientfd, &length, 4);
        CHECK_FUNCTION_FAILED(ret, "sendwait (disassembler buffer size)")
    
        // Send the data
        ret = sendwait(bss, clientfd, disassemblerBuffer, length);
        CHECK_FUNCTION_FAILED(ret, "sendwait (disassembler buffer)")
    
        free(disassemblerBuffer);
    
        break;
    }
    For formatting the result I'm using this custom function:
    Code:
    void formatDisassembled(char *format, ...) {
        disassemblerBuffer = malloc(DISASSEMBLER_BUFFER_SIZE); // Allocate the global buffer
        __os_snprintf((char *) disassemblerBuffer, DISASSEMBLER_BUFFER_SIZE, format); // Fill it using format strings
    }
    Essentially, I'm sending the WIi U the starting address, ending address, disassembler options and read the disassembled bytes e.g. like this:
    Code:
    public String disassembleRange(int address, int length, DisassemblerOptions disassemblerOptions) throws IOException
    {
        AddressRange.assertValidAccess(address, length, MemoryAccessLevel.READ);
    
        try (CloseableReentrantLock ignored = reentrantLock.acquire())
        {
            // Send the command, starting address, ending address and disassembler options
            sendCommand(Command.MEMORY_DISASSEMBLE);
            dataSender.writeInt(address);
            dataSender.writeInt(address + length);
            dataSender.writeInt(disassemblerOptions.getValue());
            dataSender.flush();
    
            // Read the buffer size and data
            byte[] disassembledBytes = readBytes();
    
            return new String(disassembledBytes);
        }
    }
    The output for the example call
    Code:
    MemoryReader memoryReader = new MemoryReader();
    String disassembled = memoryReader.disassembleRange(0x01000000, 12, DisassemblerOptions.DEFAULT);
    is the following:
    Code:
    0x00000004    0x00000000 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
    
    which is not correct. It doesn't crash either.

    The real disassembly should look similar to this so I want to see li r0, 24064, sc and blr at least:
    [​IMG]

    Any help on what I'm possibly doing wrong? Thanks :)

    @wj44
    @dimok
    @Maschell
    @QuarkTheAwesome
    @FIX94
     
    Last edited by BullyWiiPlaza, Jan 2, 2017
  10. QuarkTheAwesome

    QuarkTheAwesome Working for Hugs

    Member
    765
    1,867
    Apr 19, 2015
    Australia
    Stuck in the PowerPC
    Woah! I thought my code was riding off a quirk in the calling convention... :3
    What's happened is that you've basically done an exploit on yourself - no arguments were passed into snprintf, but the format string demanded them. Because of the way the PowerPC is put together, it's ended up reading internal values from the malloc call instead.
    There's two ways around this -
    - Change the method signature back and re-add the arguments to snprintf
    - Move to vasnprintf and va_list (although I don't know how well this'd work in this context)

    My code was written as an exception handler, so it does enjoy overwriting random things. It did originally end in an OSFatal, after all ;D
     
    BullyWiiPlaza likes this.
  11. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,693
    1,377
    Aug 2, 2014
    Germany
    Ah, true. I just didn't know what was expected so it ended up popping 2 elements from the stack since the format string apparently had 2 parameter references? I might just try your code again and see if I get it to work without corrupting anything :P
     
    Last edited by BullyWiiPlaza, Jan 3, 2017
  12. QuarkTheAwesome

    QuarkTheAwesome Working for Hugs

    Member
    765
    1,867
    Apr 19, 2015
    Australia
    Stuck in the PowerPC
    Also do keep in mind that the helper gets called several times.
     
  13. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,693
    1,377
    Aug 2, 2014
    Germany
    Since DisassemblePPCRange() uses different format strings (!), we shouldn't hard code them like you did. The following is from the Snowman decompiler in IDA Pro:
    Code:
    // uint32_t*, void *, void *
    ctr16("0x%08x    0x%08x    %s\n", r25_14, static_cast<uint64_t>(*r25_14), r6_11);
    
    // uint32_t*, void *, void *
    ctr16("0x%08x    %s\n", r25_14, reinterpret_cast<int64_t>(r1_6) + 8, r6_11);
    
    // void *, int, void *
    ctr16("%s\n", reinterpret_cast<int64_t>(r1_6) + 8, 64, r6_11);
    I worked out a different code snippet for the printing now using variable arguments:
    Code:
    char *disassemblerBuffer;
    void *disassemblerBufferPointer;
    
    void formatDisassembled(char *format, ...) {
        if (!disassemblerBuffer) {
            disassemblerBuffer = malloc(DISASSEMBLER_BUFFER_SIZE);
            ASSERT_ALLOCATED(disassemblerBuffer, "Disassembler buffer")
            disassemblerBufferPointer = disassemblerBuffer;
        }
    
        va_list variableArguments;
        va_start(variableArguments, format);
        char *temporaryBuffer;
        int printedBytesCount = vasprintf(&temporaryBuffer, format, variableArguments);
        ASSERT_ALLOCATED(temporaryBuffer, "Temporary buffer")
        ASSERT_MINIMUM_HOLDS(printedBytesCount, 1, "Printed bytes count")
        va_end(variableArguments);
    
        // Do not smash the buffer
        long projectedSize = (void *) disassemblerBuffer - disassemblerBufferPointer + printedBytesCount;
        if (projectedSize < DISASSEMBLER_BUFFER_SIZE) {
            memcpy(disassemblerBuffer, temporaryBuffer, printedBytesCount);
            disassemblerBuffer += printedBytesCount;
        }
    
        free(temporaryBuffer);
    }
    Then of course the buffer is sent:
    Code:
    // Send the disassembler buffer size
    int length = DISASSEMBLER_BUFFER_SIZE;
    ret = sendwait(bss, clientfd, &length, 4);
    ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (disassembler buffer size)")
    
    // Send the data
    ret = sendwait(bss, clientfd, disassemblerBufferPointer, length);
    ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (disassembler buffer)")
    I didn't test it yet but it looks decent now and the method signatures match also. This may be the way to go :)
     
    Last edited by BullyWiiPlaza, Jan 3, 2017
  14. BullyWiiPlaza
    OP

    BullyWiiPlaza Nintendo Hacking <3

    Member
    1,693
    1,377
    Aug 2, 2014
    Germany
    Oh yeah, that worked perfectly now. Nice. I can disassemble with all options:
    Code:
    0x01042900    0x7fc3f378    or         r3,r30,r30
    0x01042904    0x4bff9be5    bl         coreinit.rpl|__OSUnlockScheduler (0x103c4e8)
    0x01042908    0x7fa3eb78    or         r3,r29,r29
    0x0104290c    0x4bff0771    bl         coreinit.rpl|OSRestoreInterrupts (0x103307c)
    0x01042910    0x7fe3fb78    or         r3,r31,r31
    0x01042914    0xbb61000c    lmw        r27,12(sp)
    0x01042918    0x80010024    lwz        r0,0x24(sp)
    0x0104291c    0x7c0803a6    mtspr      8,r0
    0x01042920    0x38210020    addi       sp,sp,0x20
    0x01042924    0x4e800020    bclr       20,0
    0x01042928    0x7c0802a6    mfspr      r0,8
    0x0104292c    0x9421ffe8    stwu       sp,-24(sp)
    0x01042930    0x93c10010    stw        r30,16(sp)
    0x01042934    0x7c9e2378    or         r30,r4,r4
    0x01042938    0x93a1000c    stw        r29,12(sp)
    0x0104293c    0x93810008    stw        r28,8(sp)
    0x01042940    0x93e10014    stw        r31,0x14(sp)
    0x01042944    0x9001001c    stw        r0,0x1c(sp)
    0x01042948    0x7c7d1b78    or         r29,r3,r3
    0x0104294c    0x3be00000    addi       r31,r0,0
    0x01042950    0x4bff0615    bl         coreinit.rpl|OSDisableInterrupts (0x1032f64)
    0x01042954    0x2c1d0000    cmpwi      cr0,r29,0x0
    0x01042958    0x7c7c1b78    or         r28,r3,r3
    0x0104295c    0x41820044    b          2,12,0x10429a0
    0x01042960    0x2c1e0000    cmpwi      cr0,r30,0x0
    0x01042964    0x4182003c    b          2,12,0x10429a0
    With function names, damn son :D
     
    Last edited by BullyWiiPlaza, Jan 5, 2017
    QuarkTheAwesome and Maschell like this.