Homebrew RPC Range Disassembler Help

BullyWiiPlaza

Nintendo Hacking <3
OP
Member
Joined
Aug 2, 2014
Messages
1,932
Trophies
0
XP
2,467
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,908
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,467
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,908
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,467
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,467
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,908
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,467
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,467
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,908
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,467
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,908
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,467
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,467
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
  • The Real Jdbye @ The Real Jdbye:
    the vram is one advantage when it comes to AI but ends up being slower even with that and really AI is the only use case that needs more than 12gb vram right now
  • Psionic Roshambo @ Psionic Roshambo:
    Interesting lol
  • Psionic Roshambo @ Psionic Roshambo:
    I think I watched a video where two games at 4K where eating just over 16GB of RAM and it's the one case where the 7900XT and XTX pulled ahead (minus RTX of course)
  • Psionic Roshambo @ Psionic Roshambo:
    So my opinion is that they could age a bit better in the future, and maybe AMD will continue improving them via drivers like they tend to do. No guarantee there but they have done it in the past. Just a feeling I have.
  • The Real Jdbye @ The Real Jdbye:
    cyberpunk at 4k without DLSS/fidelityfx *might* exceed 12gb
    +1
  • The Real Jdbye @ The Real Jdbye:
    but that game barely runs at native 4k
  • Psionic Roshambo @ Psionic Roshambo:
    I think it was some newer games and probably poorly optimized PS4 or PS5 ports
  • The Real Jdbye @ The Real Jdbye:
    they definitely will age better but i feel dlss might outweigh that since it looks about as good as native resolution and much less demanding
    +1
  • Psionic Roshambo @ Psionic Roshambo:
    When I played Cyberpunk on my old 2080 Ti it sucked lol
  • The Real Jdbye @ The Real Jdbye:
    AMD could introduce something comparable to DLSS but nvidia's got a lot more experience with that
  • The Real Jdbye @ The Real Jdbye:
    least amd 7xxx has tensor cores which the previous generations didn't so there is the potential for AI upscaling
  • Psionic Roshambo @ Psionic Roshambo:
    They have FSR or whatever it's called and yeah it's still not great
  • The Real Jdbye @ The Real Jdbye:
    so AMD seem to finally be starting to take AI seriously
  • Psionic Roshambo @ Psionic Roshambo:
    Oh yeah those new 8000 CPUs have AI cores built in that's interesting
  • Psionic Roshambo @ Psionic Roshambo:
    Maybe they plan on offloading to the CPU?
  • Psionic Roshambo @ Psionic Roshambo:
    Would be kinda cool to have the CPU and GPU working in random more
  • Psionic Roshambo @ Psionic Roshambo:
    Tandem even
  • The Real Jdbye @ The Real Jdbye:
    i think i heard of that, it's a good idea, shouldn't need a dedicated GPU just to run a LLM or video upscaling
  • The Real Jdbye @ The Real Jdbye:
    even the nvidia shield tv has AI video upscaling
  • The Real Jdbye @ The Real Jdbye:
    LLMs can be run on cpu anyway but it's quite slow
  • BakerMan @ BakerMan:
    Have you ever been beaten by a wet spaghetti noodle by your girlfriend because she has a twin sister, and you got confused and fucked her dad?
  • Psionic Roshambo @ Psionic Roshambo:
    I had a girlfriend who had a twin sister and they would mess with me constantly.... Until one chipped a tooth then finally I could tell them apart.... Lol
  • Psionic Roshambo @ Psionic Roshambo:
    They would have the same hair style the same clothes everything... Really messed with my head lol
  • Psionic Roshambo @ Psionic Roshambo:
    @The Real Jdbye, I could see AMD trying to pull off the CPU GPU tandem thing, would be a way to maybe close the gap a bit with Nvidia. Plus it would kinda put Nvidia at a future disadvantage since Nvidia can't make X86/64 CPUs? Intel and AMD licensing issues... I wonder how much that has held back innovation.
    Psionic Roshambo @ Psionic Roshambo: @The Real Jdbye, I could see AMD trying to pull off the CPU GPU tandem thing, would be a way to...