android-ponewheel icon indicating copy to clipboard operation
android-ponewheel copied to clipboard

Pint Support

Open ghost opened this issue 5 years ago • 62 comments

Hey all,

I just got my pint in the mail today and suprise suprise my nor any 3rd party app works with it as far as I know. It is the same behavior as when gemini first dropped, where no values can be read unless the board is first unlocked by the official app.

I suspect the hash seed/key (not sure on terminology) just changed and the process is still the same but I'm not sure, I intend to investigate when I can.

I know for gemini we had a crack team working in a thread on here and I was kinda hoping to reassemble the gang, I wasn't sure how else to contact everyone. I can test anything anyone thinks of if I'm the only one with a pint board so far.

ghost avatar Dec 03 '19 04:12 ghost

I have the same problem. 🙂

TomasHubelbauer avatar Dec 03 '19 05:12 TomasHubelbauer

Ok so update, I created and hci log of the official app connecting to a pint, got it off my phone, and I'm digging through it on my PC, all of which was way harder than expected. I am totally out of my element, have no idea what I'm doing, and am just kinda trying stuff to see if anything works. If any of yall wanna look though it your more than welcome, here is the log. btsnoop_hci.log

ghost avatar Dec 04 '19 02:12 ghost

Ok so, if I'm reading this correctly, this is what happens. First thing it tries is to read the ridemode, and that returns 0 because the board is locked. I'm assuming this is a test to see if the board needs to be unlocked. It then requests the hardware & firmware revisions, and both of those work. It then turns on notifications for ridemode & lightmode for some reason, and then without any prompt from the board, sends 098e56c8c595bc9423ce87aea3bc3a45738c4278 to [UUID: e659f3ffea9811e3ac100800200c9a66], which I've never seen before. After that it writes 0 to the speed characteristic, which is weird, and then starts requesting characteristics as normal and they return correct values.

I have 2 logs and they both have the same behavior, and send the same value every time. this could be a lot easier than gemini. I'm out of time to actually try it today but let me know if anyone has luck with this.

ghost avatar Dec 04 '19 03:12 ghost

Welp, that was an order of magnitude easier than I thought it would be, I have it working lol. Heres the workflow I'm using now:

First, check the firmware revision. If less than 4034, we're all good. if >= 4034 && < 5000, do the normal thing for gemini. If >= 5000, write 098e56c8c595bc9423ce87aea3bc3a45738c4278 as a byte array to the characteristic with UUID e659f3ff-ea98-11e3-ac10-0800200c9a66. In the oncharacteristicwrite callback for that UUID, you can start requesting values and everything works.

I accidentally left in my code from gemini for sending the key challenge every 15 seconds, and that worked and keeps the connection open, so that part seems to be the same.

If someone could double check all this for me that would be great lol, I still can't believe I figured this out, it works, and that it was that easy

ghost avatar Dec 04 '19 22:12 ghost

I wonder how the shaping mode switching works with the pint?

On Wed, Dec 4, 2019, 3:52 PM Nanoux [email protected] wrote:

Welp, that was an order of magnitude easier than I thought it would be, I have it working lol. Heres the workflow I'm using now:

First, check the firmware revision. If less than 4034, we're all good. if

= 4034 && < 5000, do the normal thing for gemini. If >= 5000, write 098e56c8c595bc9423ce87aea3bc3a45738c4278 as a byte array to the characteristic with UUID e659f3ff-ea98-11e3-ac10-0800200c9a66. In the oncharacteristicwrite callback for that UUID, you can start requesting values and everything works.

I accidentally left in my code from gemini for sending the key challenge every 15 seconds, and that worked and keeps the connection open, so that part seems to be the same.

If someone could double check all this for me that would be great lol, I still can't believe I figured this out, it works, and that it was that easy

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ponewheel/android-ponewheel/issues/109?email_source=notifications&email_token=ADY56OMLKZYTP3MY6EVGN4LQXAYDRA5CNFSM4JURU5MKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEF6Y66Y#issuecomment-561876859, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADY56OJJGNRF36ZPQFJDAI3QXAYDRANCNFSM4JURU5MA .

muellergit avatar Dec 04 '19 22:12 muellergit

Its the same, theres 4 custom shaping modes, you can set values 5,6,7,and 8, which correspond to Redwood, Pacific, Elevated, and Skyline. I tried setting it to custom shaping and it didnt work lol, worth a shot.

ghost avatar Dec 04 '19 23:12 ghost

I've try to validate your findings in my Web Bluetooth based version this weekend! I'm super excited to try it, hope it works for me as well. Thanks for finding all this out!

TomasHubelbauer avatar Dec 04 '19 23:12 TomasHubelbauer

So you can use the existing Ponewheel to change the mode once it is connected with your new code?

On Wed, Dec 4, 2019, 4:05 PM Nanoux [email protected] wrote:

Its the same, theres 4 custom shaping modes, you can set values 5,6,7,and 8, which correspond to Redwood, Pacific, Elevated, and Skyline. I tried setting it to custom shaping and it didnt work lol, worth a shot.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ponewheel/android-ponewheel/issues/109?email_source=notifications&email_token=ADY56OKJD5FK5TOE2WSCN7DQXAZSXA5CNFSM4JURU5MKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEF62CBI#issuecomment-561881349, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADY56OIPLS3QXT7T7TFCSITQXAZSXANCNFSM4JURU5MA .

muellergit avatar Dec 04 '19 23:12 muellergit

Well maybe I don't know, it worked right away in my app, the labels were just wrong. Fixing that rn.

ghost avatar Dec 04 '19 23:12 ghost

That array you send may specific to your board. If I read correctly, the number my app sends is only identical for the first 3 bytes (6 hex digits).

tekkies avatar Dec 05 '19 03:12 tekkies

Well damn, I guess life is never that easy. That would make sense that it's different per board, but the app never requests board specific information before the key is sent, so I assumed it was hard coded. Maybe they are using the board's Bluetooth broadcast id or name or something? I don't know, unless someone makes a super lucky guess we're probably gonna have to tear the official app apart and see what it's doing.

ghost avatar Dec 05 '19 13:12 ghost

@Nanoux did you push your changes to a branch somewhere? Even hard coding for my board would be super useful. Do you want a copy of my trace for comparison?

tekkies avatar Dec 05 '19 14:12 tekkies

Oh I just added like 5 lines to my app, I haven't put anything up yet. AFAIK ponewheel uses my connection code, so you might be able to just drop it in. I'll post it when I get home if ya really need it.

Also ya more traces would be great, especially for boards that aren't mine.

ghost avatar Dec 05 '19 16:12 ghost

The board's last 6 digits of the serial number is sent as the Bluetooth broadcast name OWxxxxxx.

On Thu, Dec 5, 2019, 9:36 AM Nanoux [email protected] wrote:

Oh I just added like 5 lines to my app, I haven't put anything up yet. AFAIK ponewheel uses my connection code, so you might be able to just drop it in. I'll post it when I get home if ya really need it.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ponewheel/android-ponewheel/issues/109?email_source=notifications&email_token=ADY56OL4ABGEB6LVHHMG5XTQXEUYRA5CNFSM4JURU5MKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEGBKARY#issuecomment-562208839, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADY56OJYYO7IWBXCRE53T4DQXEUYRANCNFSM4JURU5MA .

muellergit avatar Dec 05 '19 21:12 muellergit

Ok update, I noticed that the official app connected way faster after the first connection, so I deleted my onewheel form the app and made a system trace of a first time pint connection, and sure enough its different!

For a first time connection, it tries to read the ridemode, gets 0, then gets the firmware & hardware revisions as before. However, after that its goes into the Gemini workflow, and sets notifications on for SerialRead, then writes the firmware version back on itself, and sure enough a flood of bytes comes flooding in gemini style. Once it gets 20 bytes it writes that same crazy string to the board, and then turns off SerialRead notifications, and then requests values as normal. I only have one log of this because its an absolute pain in the ass to get them on android and I'm still not clear on when they are actually generated, but I suspect the byte dump sent by SerialRead is the same every time since the app caches the key for subsequent connections. Since the process is identical to Gemini but apps still broke I assume they probably just changed the hash seed/key thing as I originally suspected. I'm going to have to dig through the gemini thread and see how they found that key the first time and see if I can do the same...

As for code to hard-code connect to one pint, heres all the bits I added:

@Override public void onServicesDiscovered(BluetoothGatt gatt, int status){ owGatService = gatt.getService(UUID.fromString(OWDevice.OnewheelServiceUUID));

        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {//make delay to avoid weird disconnection
            @Override
            public void run() {
                mGatt.readCharacteristic(owGatService.getCharacteristic(UUID.fromString(OWDevice.OnewheelCharacteristicFirmwareRevision)));
            }
        },500);

    }

@Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic c, int status) { String characteristic_uuid = c.getUuid().toString();

        if (characteristic_uuid.equals(OWDevice.OnewheelCharacteristicFirmwareRevision)) {

            int version = Util.unsignedShort(c.getValue());
            if(version >= 5000){
                connectionMode = 2;

                byte[] plzwrk = Util.StringToByteArrayFastest("098e56c8c595bc9423ce87aea3bc3a45738c4278"); //insert your board's key here
                BluetoothGattCharacteristic lc = owGatService.getCharacteristic(UUID.fromString(OWDevice.OnewheelCharacteristicUartSerialWrite));
                lc.setValue(plzwrk);
                boolean worked = gatt.writeCharacteristic(lc);

            }

        }
    }

@Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //Timber.i( "onCharacteristicWrite: " + status); //mOWDevice.processUUID(characteristic); if (characteristic.getUuid().toString().equals(OWDevice.OnewheelCharacteristicUartSerialWrite)){ //WERE CONNECTED MOTHER FUCKERS! whenActuallyConnected(); } }

At least I think thats it lol. I removed all code that wasn't relevant to pint but other stuff happens in those methods obviously. do everything you actually want with the board in wehnActuallyConnected().

As for toggling smartstop or whatever its called, I looked into that as well and.... its a hot mess omg. They are using the custom shaping attribute from gemini, and to turn it off you write 0300 and to turn it on you write 0301. However after each write, the app turns on notifications for that attribute, and first it sends back 0200 for some reason, and then either 0300 or 0301, whichever was just written. I assume this is verification the write worked but its jank af and I got some weird behavior in the official app toggling it on and off as fast as I could.

Heres my log for a first time connection, afaik the connection starts happening at line 1157 btsnoop_hci.log

ghost avatar Dec 05 '19 23:12 ghost

Side note for anyone smarter than me reading this, it looks to me like the pint LED strips have full RGB, in theory it might be possible to set them to any arbitrary color if the firmware supports that on the board's end, but I have no idea how to figure that out....

ghost avatar Dec 05 '19 23:12 ghost

So theres another thread over here for another app here, it doesnt look like they got as far though https://github.com/COM8/UWP-Onewheel/issues/6

ghost avatar Dec 05 '19 23:12 ghost

Well I tried just straight up using the gemini code but with the first 3 bytes switched out for the ones in my board's pint code, still didn't get the correct response, so the hash key probably did actually change.

I'm gonna ping @kwatkins , @beeradmoore, and @COM8, since you all may be interested in how far I've gotten and I think I need your help to finish this off.

ghost avatar Dec 06 '19 20:12 ghost

Ohooo, nice work!

I had similar issues when trying to get OWCE to connect to the board, but I was also half way through re-writing all the BT code so that probably didn't help 😂

I'm working on getting that BT code finished this weekend to get a build out to users, but then I'll revisit this thread and work on the Pint update.

I like the RGB LED thing, but I wouldn't want to go anywhere near having to load custom firmware (or even load current stable firmware) as to not get FM to dislike us more than they currently do.

Do you have somewhere I can DM/PM/Email you? I have an idea.

beeradmoore avatar Dec 07 '19 01:12 beeradmoore

Ya my email is [email protected] and my discord is Nanoux #6524

ghost avatar Dec 07 '19 01:12 ghost

This additional hack (in blue) got my Pint to connect if I have "Default to OW+" enabled. The key came from my bluetooth hci log running regular app (it's my personal key - see above).

image

Update: It continued to stream data to the app for about 15 mins before I moved out of range. Update: Unobscure a bit more of the key

tekkies avatar Dec 21 '19 19:12 tekkies

That key is generated in the lines above it after retrieving information from the serial read characteristic. The key may work for yours but we need to figure it out on everyone elses device. Maybe the start of the key changed from 43:52:58 to 09:83:5.

I'm around thinking with some stuff today, I'll see what my log says when I connect to my Pint.

beeradmoore avatar Dec 21 '19 23:12 beeradmoore

The start of the key definitely changed but that's not the only thing unfortunately, I tried that :/

ghost avatar Dec 22 '19 14:12 ghost

I saw a comment that the said the latest firmware requires an API call to get the key for you own OW. If that is the case, can we make that API call or would that be considered hacking?

tekkies avatar Dec 24 '19 01:12 tekkies

There is nothing to say that FM won't say it is unauthorized use of their API, but it would not likely stand up in court (I am not a lawyer). That puts the plaintiff in a powerful position however, b/c who wants to be the defendant. I don't know why FM is going to these lengths, but it does seem likely that they are trying to stop 3rd party apps. However, it is possible that is not their goal, and the reason for this change is that this is the first step in their efforts to help locate and/or brick stolen boards. Without knowing their motivation, we can only guess as to what their reaction would be to having this app connect to their API.

Regardless, someone should trace their network traffic to see what the API call is. Once we have the call which was made and the device information the connection was made for, then we can figure out what the call looks like. We need to know how we would make the API call, even if we don't plan on adding it to the code.

If FM is sending the serial number exactly as it is on the board to a RESTful API open to the world on their server, then they don't really have a case to say another app cannot do that.

If they are encrypting or salting a hash with a private key to obfuscate the serial number before sending it to their API, then that could change things. Although, I still think that is fine.

It is also possible that the keys are generated based on the serial number and we could just reverse engineer that, then code it into the app. To guess at any of that, we would need to send a group of serial numbers into the API and see what comes back.

I wish there was a security setting where you could copy a preference from another app, then we could just ensure people connect with the official app first, and pull it in. But alas, I doubt that will ever be allowed on android.

biell avatar Dec 24 '19 02:12 biell

I updated the image above to show a bit more of my key. image

tekkies avatar Dec 24 '19 02:12 tekkies

@biell Thanks for the write-up. I sent you a PM.

I did also wonder about parsing the btsnoop_hci.log file, but my Moto G (6) phone does not place the file in accessible storage - I have to generate the log then "Take a bug report" and mail it to myself :(.

tekkies avatar Dec 24 '19 02:12 tekkies

@tekkies , FM already consider the current handshake bypass that came with gemini hacking (or at least they were throwing around cease and desist and getting apps removed for it).

Just checked, and Pint is doing some API call. Start of the key matches the key you are sending so it is related.

beeradmoore avatar Dec 24 '19 03:12 beeradmoore

Well y'all are right, I tried a first time connect to my pint in the official app with wifi & data off and it gave me an error message, that is absolutely wild. It may be possible to do whatever that api call is doing locally in the app though like you said. I suspect it is very similar to what is happening with gemini and they don't want people to be able to reverse engineer it from the app. Its pretty hard for me to imagine they aren't trying to lock us out at this point to be honest.

ghost avatar Dec 24 '19 05:12 ghost

(Being forced to their API would help them get a handle on warranty start dates.)

tekkies avatar Dec 24 '19 05:12 tekkies