I converted an Android app to a webpage

I converted an Android app to a webpage

我把一个安卓应用改造成了网页

Why is this an “app”? This summer, the kids’ performing arts school are singing and dancing in a show at Disneyland. We’re all very excited, but my excitement, at least, was muted a little when I was told to install the “Travelbound” app in order to get access to the itinerary, travel arrangements, and accommodation details. Fuck that noise. This should have been a webpage. Why do you want me to install a(nother) shitty app just to tell me something that could have been a (smaller, faster, more universally-accessible) document?

为什么这东西非得是个“App”?今年夏天,孩子们所在的表演艺术学校要去迪士尼参加演出。我们都很兴奋,但当被告知必须安装“Travelbound”应用才能查看行程、交通安排和住宿详情时,我的兴奋劲儿多少被浇灭了一些。去他的吧。这本该是个网页才对。为什么非要让我安装(又一个)垃圾应用,仅仅是为了查看那些本可以做成(更小、更快、更通用)文档的内容?

I’m not remotely exaggerating. This app is literally text, images, and links to PDF files, delivered via the Web. There only seem to be two things that this “app” does, that a webpage might not have, and they’re both anti-features: It reports tracking data associated with your Google Account back to the developers. It shows you advertisements (which they call “inspirations”) for other trips organised by the same agency. Fuck. Everything. About. That.

我一点都没有夸张。这个应用本质上就是通过网络传输的文本、图片和 PDF 文件链接。这个“App”似乎只有两点功能是网页可能不具备的,而且这两点全是反人类的功能:它会将与你谷歌账号关联的追踪数据回传给开发者;它会向你展示(他们称之为“灵感”)由同一机构组织的其它旅行广告。去他的。这一切的一切。

A webpage would have been so much better. Unlike this app, a webpage can be…

  1. Copy-pastable
  2. Printable
  3. Saveable
  4. Bookmarkable
  5. Searchable
  6. Usable on virtually any device
  7. (Potentially) more-accessible

网页明明要好得多。与这个应用不同,网页可以……

  1. 复制粘贴
  2. 打印
  3. 保存
  4. 加入书签
  5. 搜索
  6. 在几乎任何设备上使用
  7. (潜在地)更易于访问

I’m annoyed enough… that I’m going to “fix” this app. Hold my beer. Intercepting app traffic It’s been a while since the last time I reverse-engineered an Android app from its network traffic, so I had to brush-up on the best way. Here’s what I ended up doing.

我实在太火大了……以至于我决定“修复”这个应用。看我的吧。 拦截应用流量 距离我上次通过网络流量逆向工程安卓应用已经过去很久了,所以我不得不温习一下最佳实践。以下是我最终的操作步骤。

Created a new virtual device in Android Studio’s Virtual Device Manager. Tested adb shell was working and used rootAVD to root it: ./rootAVD.sh system-images/android-33/google_apis_playstore/x86_64/ramdisk.img. Performed a cold boot, ran Magisk, and tweaked its settings to automatically grant su access to any app that asked.

在 Android Studio 的虚拟设备管理器中创建了一个新的虚拟设备。测试了 adb shell 是否正常工作,并使用 rootAVD 对其进行了 Root:./rootAVD.sh system-images/android-33/google_apis_playstore/x86_64/ramdisk.img。执行冷启动,运行 Magisk,并调整其设置,自动授予任何请求 su 权限的应用以 root 权限。

All your traffic are belong to me. At this point, it’s just like running Wireshark or TCPdump. Ran HTTP Toolkit and told it to intercept AVD traffic. It installed a (fake) VPN provider, routing the phone’s traffic through the proxy. Installed the Travelbound app from the Play Store. Configured HTTP Toolkit to proxy only the Travelbound app (more signal, less noise).

你的所有流量都归我了。此时,这就像运行 Wireshark 或 TCPdump 一样。运行 HTTP Toolkit 并让它拦截 AVD 流量。它安装了一个(伪造的)VPN 提供商,将手机流量通过代理路由。从 Play Store 安装了 Travelbound 应用。配置 HTTP Toolkit 仅代理 Travelbound 应用(信号更多,噪音更少)。

With only a couple of minutes experimentation I discovered that the app works by concatenating the username and password and using it in a URL of the form: https://travelbound.api.vamoos.com/api/itineraries/{username}-{password} This returns a pile of JSON which, with a little interpretation, can be seen to represent all of the content the app “shows”. E.g., there’s: an array containing each leg of the itinerary, an array containing all of the “inspirations” advertisements to show you, a cross-referenced array containing all of the files (images etc.) that are referenced by the other sections, etc. They’re clearly producing HTML code anyway… so again, I ask: why isn’t this a webpage?

经过几分钟的实验,我发现该应用的工作原理是将用户名和密码拼接,并将其用于如下格式的 URL:https://travelbound.api.vamoos.com/api/itineraries/{username}-{password} 这会返回一堆 JSON 数据,稍加解读就能看出,它代表了该应用“展示”的所有内容。例如:包含行程每一段的数组、包含所有要展示给你的“灵感”广告的数组、包含其他部分引用的所有文件(图片等)的交叉引用数组等。他们显然已经在生成 HTML 代码了……所以我再次发问:为什么这不能是个网页?

A little experimentation showed me that the S3 image URLs were being delivered with moderately-short expiration times, so the JSON needs re-fetching periodically even if the content hasn’t been changed.

稍作实验后我发现,S3 图片 URL 的过期时间较短,因此即使内容没有改变,也需要定期重新获取 JSON。

Turning it into something better Now I had everything I needed to make something… better. I wrote a Ruby script that runs on a Cron schedule to pull the latest JSON and use it to build a HTML page. I chose to have it completely skip over the “inspirations” (“overlayRows” in the data schema) and just list: the items from the itinerary and all of the files not referenced by the inspirations nor itinerary, (a lazy way to collate the PDF download links).

把它变得更好 现在我拥有了制作更好东西所需的一切。我写了一个 Ruby 脚本,通过 Cron 定时任务运行,拉取最新的 JSON 并用它构建一个 HTML 页面。我选择完全跳过“灵感”(数据模式中的“overlayRows”),只列出:行程中的项目,以及所有未被“灵感”或行程引用的文件(这是一种整理 PDF 下载链接的偷懒方法)。

Then I hosted the page, protected by a password: the same one my tour group were given in the first place. I included the raw JSON it used in

elements so it can be checked if e.g. there are bits of the schema I didn’t see but that might appear later. My web page isn’t as “pretty” as the app from which it “borrows” its information. But it’s a fraction of the size and gets all of the Web’s standard features for free.

然后我托管了这个页面,并设置了密码保护:就是我们旅行团最初收到的那个密码。我将它使用的原始 JSON 包含在

元素中,以便在出现我未发现但后续可能出现的模式片段时进行检查。我的网页虽然没有它“借用”信息的那个应用那么“漂亮”,但它的体积只有后者的几分之一,并且免费获得了所有网页的标准功能。

Some people like an “app”, and that’s… fine, I guess. But some apps could have been a webpage. And especially where, like this one, the content they deliver is already written in HTML and delivered over HTTP… they should be a webpage, right? I can’t understand how we got to this place with “app culture”! Software companies are happy to make their lives harder (and more expensive: deploying to the big app stores isn’t free!), in order to deliver HTML content to fewer people and with fewer features than if they just published directly to the Web in the first place!

有些人喜欢“App”,这……好吧,我没意见。但有些应用本可以做成网页。特别是像这个例子一样,它们交付的内容本身就是用 HTML 编写并通过 HTTP 传输的……它们本该就是网页,对吧?我无法理解我们是如何陷入这种“App 文化”的!软件公司宁愿让自己的工作变得更困难(也更昂贵:发布到大型应用商店可不是免费的!),也要把 HTML 内容交付给更少的人,且功能比直接发布到网页上还要少!

There are (some) tasks for which an “app” is absolutely the right choice of medium. Travelbound is not one of them. But at least I (and the rest of our group, whom I’ve shared it with) now get the choice about how we access this content. Either a 43MB app (ballooning to 124MB when it’s finished downloading extra content) with tracking and advertisements… or a 0.05MB web page (with an optional extra 35MB of images) that provides more features and works on more devices. I know which one I’ll be using!

确实有(一些)任务,“App”绝对是正确的媒介选择。但 Travelbound 显然不是其中之一。不过至少我(以及我分享给的其他团员)现在有了选择访问方式的权利。要么是一个 43MB(下载完额外内容后膨胀到 124MB)且带有追踪和广告的应用……要么是一个 0.05MB(外加可选的 35MB 图片)的网页,它提供了更多功能,且能在更多设备上运行。我知道我会选哪一个!