Hacking Question Payload loader for iOS?

Traiver

Developer
Developer
Joined
Aug 1, 2014
Messages
1,326
Trophies
1
Location
???
XP
2,949
Country
United States
Just upgrade to an Android, or use Windows. Best methods.
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.
 
  • Like
Reactions: OkazakiTheOtaku

_______

 
Member
Joined
May 13, 2016
Messages
515
Trophies
0
XP
834
Country
Japan
Don't be so hostile bro. >w>
I just don't see why people would waste their time trying to make closed hardware and software do shit it was never intended to do, when people usually have old Android phones laying around in drawers, not being used that would be perfect for this stuff.

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.
 

Enovale

Hey. I exist. Woo
OP
Member
Joined
Jul 12, 2016
Messages
833
Trophies
0
Location
Narnia
XP
946
Country
United States
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
 

Mythrandir

Life-long Learner
Member
Joined
Nov 12, 2015
Messages
183
Trophies
0
XP
868
Country
United States
I just don't see why people would waste their time trying to make closed hardware and software do shit it was never intended to do
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,

warmo161

Well-Known Member
Member
Joined
Nov 3, 2011
Messages
160
Trophies
1
Location
A house
XP
1,185
Country
United Kingdom
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?
 

JustBrandonT

Well-Known Member
Newcomer
Joined
Mar 11, 2018
Messages
75
Trophies
0
Age
34
XP
518
Country
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,

Lil_SpazJoekp

Well-Known Member
Newcomer
Joined
Apr 11, 2018
Messages
89
Trophies
0
Age
27
XP
373
Country
United States
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.

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,

JustBrandonT

Well-Known Member
Newcomer
Joined
Mar 11, 2018
Messages
75
Trophies
0
Age
34
XP
518
Country
Canada
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?

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.
 

Lil_SpazJoekp

Well-Known Member
Newcomer
Joined
Apr 11, 2018
Messages
89
Trophies
0
Age
27
XP
373
Country
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.
Ohh okay so we're only supposed to use eakit?

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.
Cool..would it be able to use testflight?
 

JustBrandonT

Well-Known Member
Newcomer
Joined
Mar 11, 2018
Messages
75
Trophies
0
Age
34
XP
518
Country
Canada
Ohh okay so we're only supposed to use eakit?

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,

snoofly

Well-Known Member
Member
Joined
Aug 18, 2015
Messages
1,012
Trophies
0
Age
54
XP
2,133
Country
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,

Lil_SpazJoekp

Well-Known Member
Newcomer
Joined
Apr 11, 2018
Messages
89
Trophies
0
Age
27
XP
373
Country
United States
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!
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.
 

JustBrandonT

Well-Known Member
Newcomer
Joined
Mar 11, 2018
Messages
75
Trophies
0
Age
34
XP
518
Country
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,
  • Like
Reactions: softwareengineer

softwareengineer

Well-Known Member
Newcomer
Joined
Apr 17, 2018
Messages
75
Trophies
0
Age
39
XP
217
Country
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.

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.

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,
  • Like
Reactions: Dread_Pirate_PJ

apple_juice

Member
Newcomer
Joined
May 3, 2018
Messages
5
Trophies
0
Age
29
XP
118
Country
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,

Enovale

Hey. I exist. Woo
OP
Member
Joined
Jul 12, 2016
Messages
833
Trophies
0
Location
Narnia
XP
946
Country
United States
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
 

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • No one is chatting at the moment.
    OctoAori20 @ OctoAori20: Nice nice-