-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pint Support #109
Comments
I have the same problem. 🙂 |
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 |
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. |
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 |
I wonder how the shaping mode switching works with the pint?
…On Wed, Dec 4, 2019, 3:52 PM Nanoux ***@***.***> 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
<#109?email_source=notifications&email_token=ADY56OMLKZYTP3MY6EVGN4LQXAYDRA5CNFSM4JURU5MKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEF6Y66Y#issuecomment-561876859>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ADY56OJJGNRF36ZPQFJDAI3QXAYDRANCNFSM4JURU5MA>
.
|
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. |
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! |
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 ***@***.***> 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
<#109?email_source=notifications&email_token=ADY56OKJD5FK5TOE2WSCN7DQXAZSXA5CNFSM4JURU5MKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEF62CBI#issuecomment-561881349>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ADY56OIPLS3QXT7T7TFCSITQXAZSXANCNFSM4JURU5MA>
.
|
Well maybe I don't know, it worked right away in my app, the labels were just wrong. Fixing that rn. |
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). |
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. |
@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? |
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. |
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 ***@***.***> 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
<#109?email_source=notifications&email_token=ADY56OL4ABGEB6LVHHMG5XTQXEUYRA5CNFSM4JURU5MKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEGBKARY#issuecomment-562208839>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ADY56OJYYO7IWBXCRE53T4DQXEUYRANCNFSM4JURU5MA>
.
|
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
@OverRide
@OverRide 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 |
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.... |
So theres another thread over here for another app here, it doesnt look like they got as far though COM8/UWP-Onewheel#6 |
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. |
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. |
Ya my email is [email protected] and my discord is Nanoux #6524 |
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). Update: It continued to stream data to the app for about 15 mins before I moved out of range. |
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. |
The start of the key definitely changed but that's not the only thing unfortunately, I tried that :/ |
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? |
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 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 , 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. |
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. |
@beeradmoore Awesome! |
I'll try when I have a chance, I'm also looking to port the unlock code. Thanks @beeradmoore So I'll share my JSON ASAP within the next few days if you don't have an example by then. As a side note, fuck this "private API misuse" bullshit. When will companies ever learn. 😖 |
An other thing: |
I believe renaming the OW only affects the display in the app but is not actually written into the board, so the Bluetooth name always remains the same. But I'm not sure this is accurate. |
@COM8 , sure it looks something like this, same length, It was not throwing "private IP blah blah" the other day when I was throwing random ID/Keys at it, so that's new 😂. But yes if you're reading this FM, block IPs of cellular phones that change on the regular, or block IPs of places with shared IP (eg. college), that'll work great and won't accidentally block anyone who just go their Pint/XR for Christmas 👍 |
That's right: they're two different things. I don't know where the custom OW name is stored exactly (within the board or on the network), but it's not in the app/device. I fix a lot of boards and, after i've connected to them, the custom name owner has chosen does appear on my app. |
Oh, so that's your fault! ;D |
What worries me about this, is what happens if FM closes shop? Having the app worked tied to the company is a very worrisome development for a lot of reasons outside of ponewheel. |
Amazing job reverse engineering that stuff! Did anyone get a chance to find out how does the unlock key change in time? It is indeed cached in the app (I tried to connect to pint through official app with no Internet and it worked). I decompiled the official app and will see if I can find how it stores the key. Anyway, the key has to be trusted by the firmware to unlock the board's BT communication. I don't think it's constant, that would be too easy (?), so I wonder what it might be. The only thing that comes to my mind atm is that it's some kind of One-Time-Password (HOTP / TOTP). The key and algorithm used to generate it needs to be written in the firmware, so that could be reverse engineered as well, I suppose. This requires some knowledge out of my scope, I wonder if you guys have tried to work with someone who knows his way around the firmware? |
I just re-checked my Pint and both the key and activation code are both the same as they were in early December. It's very likely that the key from the board is a value from using the serial as a hash. The alternative is its a second static value on the board which is the key, and FM have a giant lookup table containing serial and key. |
I would rather bet on the first possibility tbh. That would mean that the algo for the hash is in the firmware and if we could reverse engineer that, we would be free from the API. |
I'd agree. It means that whatever FM do on their side is also very likely a hash.
Some other possibilities are the key from the server is not based on ApiKey which is sent to it, but instead ApiKey is just to validate the serial is correct and exists. And therefor key from the server is 100% based on serial. (eg, we don't need to do work on ApiKey to get Key) Whatever server is doing to hash, it's most likely replicated on the board (unless they are just checking some verification bits, but I don't expect that to be the case). So I guess from a technical standpoint I wouldn't expect it to be too complex of an algorithm to make sure it can not only fit on the board but execute quickly. I have limited knowledge in cryptography and low powered CPU models like found on the OW so I don't know what we should expect there. |
For a quick sanity check (and shooting fish in a barrel) I got my serial number, converted it to byte array and did a MD5 (correct length for ApiKey) and SHA1 (correct length for Key) hash on it. Wouldn't generate the same code. Double checking this tread I can see that both the key directly from the board (ApiKey is just a subset of these values) and they key from the server both start with 098E56, and the final byte is also calculated by XORing all the previous data in the byte array. This is the same check that was done with the gemini firmware to calculate the last byte so I suspect some re-used code/hash/algorithms here.
So the value that is sent to the server (ApiKey) is trimmed of this static data (first 3 bytes and final checksum/verification byte). But what is interesting is the key returned from the server already includes both the same 3 bytes and a valid checksum value on the end. So to me this either means the server code is:
|
The key is hex, right? The prepended part Anyway, I will try to check what my key looks like when I'm free and maybe we could compare them and see if we find something interesting. |
I know this is an old issue but I was hoping to see if anyone is aware whether the methed @beeradmoore outlines is still functional for retrieving the api key? I converted the code to a small python script yet always receive the same response no matter the length of time -- |
@Tbruno25 , they totally block you after a few requests for the same token. I think your board might be blocked and they want you to call them to get a slap on the wrist. Although the overall network protocol is being weird. We launched this feature in OWCE on Jan 1st and within a week it stopped working. Even though we were spoofing absolutely everything possible FM found a way to detect and block it. The weird part is now when I launch a fresh official OW app with my Pint connected I get the same network requests which makes me thing nothing changed, however the response I get is something like What I believe Is probably happening is the keys are being backed up to the device cloud (iCloud in this instance) and we are not able to capture that on delete/reinstall. Maybe it can be dumped with some more advanced tools. That is a lot of work to get something that may not prove to be much of anything. Something new is in the works to get the key though. More info at a later date. |
@Tbruno25 , I just went and did a request on my test device that I thought I had not installed OW app on (fresh device, fresh apple account), however the App Store download icon was a cloud so I must have at some point installed this. I can't confirm there is no cloud sync funny business going on here unfortunately. But here is my full header request and response. Request header:
BOARD_SERIAL: My board serial number NOTE: Odd things happened running this with my main and my test device. My main device reported the HW version as 5300 (as above), my test device (iOS 12) reported my board as 5301. It says 5300 in board details. Other requests also reported it as a 5300. So yeah.. that's a thing. Response header:
Response body:
So the server never returned my token in activation. So either: I still lean towards A, but I have no idea how to prevent this sync (on iOS or Android) so if anyone has any tips aside from completely wipe my phone. |
@beeradmoore would a burner Apple ID work? |
Yeah, but I'm le tired. It also means I gotta spend another $8 on Charles Proxy. EDIT: Looking to see if its possible to get BT passthrough to an Android Emulator. |
I wish I knew how to fix the former, could use the solution for myself, too. But at least I can help with the latter. Do you have PayPal or would you consider setting up Github Sponsors? |
Appreciate it, but I don't want to take any money for OW endeavours. :) I just did an encrypted backup of my test phone and busted it open with mvt and found this file.
Did a base64 decode and found it to be a binary plist file, and inside it is my device serial and key (one returned from the server) as well as a GUID which I don't know is for. |
Below I did the same steps again and again. That is full backup, decrypt backup, check result files for OnewheelsList. First up was after I deleted the app. No OnewheelsList found. This was to make sure no additional data was hanging around. Installed the app, but didn't open it. OnewheelsList was not found (I am unsure of the other files listed below exited or not). After opening the app but not connecting to my board OnewheelsList was found but is empty.
If this is empty here I assume its not doing a cloud fetch on launch. Here is a list of all Onewheel/FutureMotion files extracted from a backup
The file we keep checking is Documents/Onewheels.plist. Went through setup, detected my board but did not connect to it. OnewheelsList data is still empty. Airplane mode enabled. Connected to board and got the "Online activation failed - Please ensure to have internet connection or try again later"
OnewheelsList now has data! Buuuut.... it is missing the value that would be where the key is. The UUID I mentioned earlier is the same. Turn off airplane mode. Connect to board. OnewheelsList now contains the data it originally did. Looking into some more network connections again I can see that UUID_KEY mentioned above was returned from a fcmtoken.googleapis.com/register. During the activation stage there was also a request it inappcheck.itunes.apple.com, but blocking on my network did not stop this field loading correctly. Last attempt for the night I blocked:
And now I got an activation error. So I think from this I would say something here is required for activation. EDIT 1: I went and unblocked those domains one by one and I got a successful activation after I unblocked fcmtoken.googleapis.com. After re-blocking it I got a successful activation again. So it must be some combination of these. firebaseinstallations.googleapis.com appears to be another part of this activation system. With these blocked the activation endpoint is never called. With just firebaseinstallations.googleapis.com and inappcheck.itunes.apple.com blocked I am not able to activate. Unblocking the former it now activates successfully. From this I would say if the key is stored in the cloud anywhere it is in the google cloud, not iCloud. |
Totally wild guess, based on the APIs and @beeradmoore findings: the /wp-json/fm/v2/activation endpoint takes your details, but instead of sending back the required secret, it creates a firebase cloud messaging topic using your Serial / key, and FCM pushes the secret to your device / OneWheel app The UUID_KEY is your (FCM) device registration, and is mapped to a specific board, so eg.: I buy your board, I start the FM OW App, it hits the activation endpoint, it creates a new UUID_KEY in FCM, which gets mapped to the boards serial / api key, and now the secret gets pushed to my phone instead of yours. |
I would also say it's just pushing the key to the device. I've seen this way of obscuring communication before. It means that the token will only be sent to the app with a specific app id and signed with a specific key (fcm ensures that) so there isn't an easy way to start receiving these push msgs in a 3rd party app, I think :/. Best you can do is to extract the key from shared prefs or smth on android and the plist file on ios and put it in 3rd party app, which isn't amazing ux ;) |
I don't know enough about FCM to know if it pushes via the Apple Push Notification Service (APNS). I would assume so because I see it sending my APNS device ID in some requests going to FCM. I also don't know if I say no to push notifications if that disables APNS completely. Rejecting push notifications from the prompt still allowed the board to register. If it is still allowed to come through in the background I think it's part of the socket connections the device makes back to Apple services, I am not sure how to capture them. I would agree that this is how they are obfuscating the token. Hope is not lost though. There are still a few tricks left in the bucket. As for the situation as a whole, this quote via twitter rings so very true.
|
FCM uses APNS but it doesn't really matter, app can use it directly for the same effect. As for background pushes, afaik it can be sent in background without explicit permission, although if you disable background processing in system settings it should probably not go through then. iOS and Android are managing socket connection for their push services but this is something baked into system on ios and into Google play services (or similar from huawei etc) on android. I'm pretty sure they use cert pinning for that (basic security of a critical system function) so MITM like Charles proxy is not gonna help too much. I wonder what other ideas you have :) |
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.
The text was updated successfully, but these errors were encountered: