Reverse Engineering the We Heart It API

Aswin S 28th August 2022

#weheartit #reverse engineering #api #enumeration #android #web


Jump straight to the API endpoints: Github Repo

When I eagerly looked for the API for an interesting, image-sharing app I recently found, I found nothing. This was weird because this social media network claims to have over 30 million users they haven’t even once talked about its API or design aspects of its website. Scraping the site for data works (and has been tried ) but that is an unstable backbone because it depends on the web elements to never change. So I decided to reverse engineer for the first time in my life and hopefully disprove my mind that the website is a front for an aesthetic cult.

The decompiling: Or how I learned to stop worrying and start loving my stupidity.


So the first step of reverse engineering an android app is to look at its contents and decompile them.

I downloaded the APK of the latest version of WeHeartIt and extracted them.

Fun fact: APK files are just the same as ZIP files, in fact, you can change the .apk to .zip and extract the contents inside normally.

Grepping the directory for ‘API’ reveals that certain dex binaries contain the word in them. It was my first time coming across them and found that they basically contain code that is ultimately executed by the Android Runtime. Every APK has a single classes.dex file, which references any classes or methods used within an app. Sneaky android.

I turned the dexs into a jar file using dex2jar which I admit, is a pretty cool tool. Now here’s where it gets…I don’t know, stupid?

Googling showed me that jar files are best opened (and decompiled) in a GUI called jd-gui and I was like yeah sure and I opened it. But here’s the catch, the source files apparently have a size of more than 900MB and the app couldn’t save it due to some processing bottlenecks, poor thing. All I needed was one specific folder weheartit from com. It would take lunch and a Black Mirror episode for me to figure out that just like APKs, JARs can be extracted like a ZIP file. I was furious and excited and extracted the folder from the jar, fed it to jd-gui, and finally got the source java files I have been throwing coins in a fountain for.

The Reverse Engineering


I do not know Java. I do not look forward to understanding it but I understand how it works so I was tinily optimistic about discovering something useful in the code. It was jackpot. There was an API directory that to my surprise had a file named WeHeartIt.javawhose contents kind of looked like this.

public interface WeHeartIt {
  @POST("/api/v2/collections/{collectionId}/collaborators/abandon")
  Completable abandonCollection(@Path("collectionId") long paramLong);
  
  @PUT("/api/v2/notifications/{id}")
  Single<Notification> acceptNotification(@Path("id") long paramLong, @Body String paramString);
  
  @FormUrlEncoded
  @POST("/api/v2/devices/activate")
  Completable activateDevice(@Field("device_uuid") String paramString1, @Field("advertising_id") String paramString2);
  
  @POST("/api/v2/collections/{collection_id}/entries/{entry_id}")
  Completable addEntryToCollection(@Path("collection_id") long paramLong1, @Path("entry_id") long paramLong2, @Body String paramString);
  
  @FormUrlEncoded
  @POST("/api/v2/collections/{id}/entries")
  Completable addMultipleEntriesToCollection(@Path("id") long paramLong, @Field("entry_ids[]") long[] paramArrayOflong);
  
  @GET("/api/v2/articles")
  Single<EntriesResponse> articles(@QueryMap Map<String, String> paramMap);

This single file contained every single API endpoint. I didn’t expect it to be this easy but I’m not sure what I was expecting either. Some greping and truncating with the help of Python, I had just the endpoint URIs categorized by their HTTP methods. Here’s what they look like:



Headers and payloads for each endpoint are yet to be tested but here’s what a simple login function from one of the above endpoints would look like:

courtesy of: zeviel@github

import requests

class WeHeartIt:
    def __init__(self):
        self.api = "https://api.weheartit.com"
        self.headers = {
            "user-agent": "okhttp/3.14.9",
            "x-weheartit-client": "os: 'Android', sdkVersion: '25', device: 'ASUS_Z01QD', appVersion: '9.0.1.RC-GP-Free(21892) (21892)'"
        }

    def login(self, username: str, password: str):
        data = {
            "username": username,
            "password": password,
            "signature": "af3fc56e9223a0f327cac372cd56225b",
            "grant_type": "password"
        }
        response = requests.post(
            f"{self.api}/oauth/token",
            data=data,
            headers=self.headers).json()
        if "access_token" in response:
            self.access_token = response["access_token"]
            self.headers["authorization"] = f"Bearer {self.access_token}"
        return response

And here’s how you would use it:

whi = WeHeartIt()
whi.login(username="", password="")

Conclusion


Reverse engineering is really cool. I hope to do it again someday when I want to. This simple journey helped me a lot to learn how android apps really work and how simple (the logic) it is in the backend. I will update or edit this article depending on the information I find in an “updates” section but till then, have fun being frustrated at companies for not providing APIs, folks. C’yall.