Homebrew RPC Range Disassembler Help

BullyWiiPlaza

Nintendo Hacking <3
OP
Member
Joined
Aug 2, 2014
Messages
1,932
Trophies
0
XP
2,477
Country
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
 
  • Like
Reactions: CosmoCortney
Joined
Apr 19, 2015
Messages
1,023
Trophies
1
Location
Stuck in the PowerPC
Website
heyquark.com
XP
3,909
Country
Australia
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,

BullyWiiPlaza

Nintendo Hacking <3
OP
Member
Joined
Aug 2, 2014
Messages
1,932
Trophies
0
XP
2,477
Country
Germany
I don't know how your RemoteString class works but you can see how the function wants it; sorta like printf.
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.
 
Joined
Apr 19, 2015
Messages
1,023
Trophies
1
Location
Stuck in the PowerPC
Website
heyquark.com
XP
3,909
Country
Australia
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.
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.
 
  • Like
Reactions: BullyWiiPlaza

BullyWiiPlaza

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

BullyWiiPlaza

Nintendo Hacking <3
OP
Member
Joined
Aug 2, 2014
Messages
1,932
Trophies
0
XP
2,477
Country
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,
Joined
Apr 19, 2015
Messages
1,023
Trophies
1
Location
Stuck in the PowerPC
Website
heyquark.com
XP
3,909
Country
Australia
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!
 
  • 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 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
 

BullyWiiPlaza

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


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

@wj44
@dimok
@Maschell
@QuarkTheAwesome
@FIX94
 
Last edited by BullyWiiPlaza,
Joined
Apr 19, 2015
Messages
1,023
Trophies
1
Location
Stuck in the PowerPC
Website
heyquark.com
XP
3,909
Country
Australia
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
}
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
 
  • Like
Reactions: BullyWiiPlaza

BullyWiiPlaza

Nintendo Hacking <3
OP
Member
Joined
Aug 2, 2014
Messages
1,932
Trophies
0
XP
2,477
Country
Germany
Woah! I thought my code was riding off a quirk in the calling convention... :3
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,
Joined
Apr 19, 2015
Messages
1,023
Trophies
1
Location
Stuck in the PowerPC
Website
heyquark.com
XP
3,909
Country
Australia
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
Also do keep in mind that the helper gets called several times.
 

BullyWiiPlaza

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

BullyWiiPlaza

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

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • No one is chatting at the moment.
    BakerMan @ BakerMan: @salazarcosplay yeah cod's still up