Reverse Engineering ‘Product Catalog’

Recently I’ve been looking at an application that has some data that I’d like to scrape and use/format for my own selfish desires. The application we are targeting is on iOS + Android so I went through the usual flow on my iPhone
– mitmproxy – no good. traffic must be SSL / non-HTTP. Android cert-pinning stops app on init
– wireshark w/ rvictl on iOS device – SSL traffic pulling initial data catalog / no API fetches once app is initialized (sqlite init)

After getting blocked and giving up for about a month I decided to root my OG Nvidia Shield and dig a little deeper..

Digging Deeper


Root phone -> Install Xposed Framework -> Download [Inspeckage] Module -> Get Started

My initial hope was that Inspeckage would solve all my problems and I wouldnt have to dig too much into Xposed but in the ended up being the launching pad to the golden catalog.

After turning on Inspeckage and letting the app boot up for the first time we can see a few things. (1) https traffic, (2) files created zip/certs/js+img assets. I was hoping the SQLite tab would light up and we could query against .db file immediately but that would be too easy

Ripping things apart

Before getting into the .apk I figured it would be worthwhile to see what was stored on my device
adb shell && cd /data/data/
and… explore!

The main thing I was looking for here was something in the databases/ or files/ directories. Luckily we were able to spot a TARGET.db within files/databases but it was definitely encrypted (dreaded “file is encrypted or not a database” when querying)

Remember that if the app is not debuggable you won’t be able to adb pull /pathtoDBFile.db instead you’ll have to adb shell && su && mkdir /sdcard/data && cp /pathoDBFile /sdcard/data and then pull that file

Inspeckage has the handy download .apk so there’s a few things we can do here (starting with the most obvious)
– unzip it and take a look at what we’ve got

dan@dan-MacBookPro:~/riperino$ unzip og.apk
Archive:  og.apk
  inflating: AndroidManifest.xml
  inflating: assets/_where-is-www.txt
 extracting: assets/
 extracting: assets/www/assets/
 .... a ton of assets (selected some potential highlights)
  inflating: assets/www/config/config.json
  inflating: assets/www/cordova.js
  inflating: assets/www/css/bootstrap/
  inflating: assets/www/js/
 extracting: res/drawable/icon.png
  inflating: res/xml/config.xml
 extracting: resources.arsc
  inflating: classes.dex
  inflating: lib/armeabi/
  inflating: lib/armeabi-v7a/
  inflating: lib/x86/

From looking at the contents we can tell the basic structure but we are mostly interested in a few files
– & assets/ (interesting fallback zip for sqlcipher..possible fun attack vector)
– classes.dex (exactly what it says)

This confirms our hunch about SQLite and explains the missing contents from the SQLite tab while using Inspeckage. Now the hope is that classes.dex reveals something blatantly obvious..

Reading some code

There are quite a few tools for digging into the contents of that .dex file but I’ll go over my comfortable flow (unnecessary .dex -> .jar step with tools like Bytecode Viewer but the extra step does provide some extra possibilities if we really need to build a custom apk)

Using the dex2jar toolkit we can use the classic d2j-dex2jar to give us the handy .class files zipped to .jar (while not perfect this is almost always “good enough” for what we need)

After creating the .jar its time to pop open Java Decompiler and dig into the package in question.. And after a little while we run into this section of code (masterKey makes us happy and definitely worth hooking into?)

After reading the docs for getReadableDatabase it’s clear that’s indeed the SQLite password and if the developer also followed the documentation they would also have called createAndGetDBPath which would serve as the initialization.. Lo and behold the DBHelper.class implements that class and we can add a hook to ensure we catch the key when we reboot the app

Hooking and Winning

Without getting too tangential Xposed is great and the documentation is outstanding for anyone interesting in getting started. If the development tutorial is not enough the content online was more than enough to clear up any potential blockers.

public class Main implements IXposedHookLoadPackage {
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        findAndHookMethod("com.****.core.db.DBHelper", lpparam.classLoader, "createAndGetDBPath", Context.class, String.class, new XC_MethodHook() {
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                XposedBridge.log("Hooked em': DBHelper#createAndGetDBPath ");

We load our hook module, reboot our device and when we rerun the app we see something beautiful in the logs

Hooked em': DBHelper#createAndGetDBPath
base64masterKey== (totally legit..)

Now I should be able to

Sqlcipher rippedDB.db
PRAGMA key=”base64masterKey==”

And get something other than file is encrypted or not a database. Unfortunately this didn’t work on my machine and I thought I was getting juked out and went to bed.

After thinking about questions like these (1, 2, 3) all night + the following day of family time it became clear versions must be the issue in my case. After figuring out the difference between Commercial and Community it became clear this was just a time save and the same version was being used for both and the commercial folks were just given the latest binaries without having to work for them. Since we have some compatriots who dont like working much either I ran brew install sqlcipher and of course things just worked..

Mac - 3.15.2
Ubuntu 16.04 - 3.8.6

As an additional aside there are some open source “sqlbrowser applications” that have sqlcipher as a feature but dont do a great job revealing the versioning. Just build community versions as they are released and things should be fine / bank on everyone being on commercial and brew updating at the same time 😀

Upon using the correct version we see a beautiful table list and can query to our heart’s content.


The entire sqlcipher seems incredibly trivial given the release strategy and knowing what we know about hooking. The fact that the key is going to be passed around no matter what makes things quite easy unless some additional precautions are taken but unfortunately it just moves the problem a step deeper. In the end the Android SQLDatabase methods are going to used and we can hook them no matter how many Proxys/Helpers are introduced to the codebase. After digging further into the Xposed modules someone wrote a generalized hook that looks for the specific sqlcipher native package to hook into.. This seems to reinforce my belief that it’s general uselessness but there must be a reason big companies are giving them money (other than the desire to avoid building it themselves)

Ixalan Draft #1

Black/Blue/White Deck

Didnt realize I messed up mana until I got home.. would have preferred a 664

  • 7 Swamp
  • 6 Island
  • 3 Plains
  • Ixalans Binding
  • Slash of Talons
  • Cancel
  • Spell Pierce
  • 3 One With the Wind
  • 2 Run Aground
  • 2 Depths of Desire
  • Duress
  • 2 Call to the Feast
  • Deadeye Plunderers
  • Anointed Deacon
  • Desperate Castaways
  • Wanted Scoundrels
  • Seekers Squire
  • Kinjallis Caller
  • Shining Aerosaur
  • Paladin of the Bloodstained
  • Prosperous Pirates
  • Sailor of Means
  • Shipwreck Looter


  • Charging Monstrosaur
  • Dire Fleet Captain
  • Kinjallis Caller
  • Gilded Sentinel
  • Lookouts Dispersal
  • Queens Commission

This is the deck that I boxed up when I headed home.. Looking at it now I can see why I lost my last 2 matches

In my first game I ran Charging Monstrosaur (swapped after game for another One With the Wind since it won my the first game..not the best idea to run 3) and the second Kinjallis Caller which was played on turn 1 and proved to be an annoyance for my opponent (swapped for a second Call to the Feast.. not a bad card but wasnt necessary)

I ended up winning both games with a timely Ixalans Binding and One With the Wind which led me into a false sense of security for my next 2 matches (both of which I think I should have won)

Connecting to Shitty Wifi on Amtrak

I wrote this post a few months ago when I was stuck on a train and figured I’d publish it before it gets lost forever.

Today I bring you the pain in the ass process I went through to get a really shitty connection on my Amtrak ride

Connecting to the wifi network yields a proper connection (ip assigned & network-manager is happy)… but once you try to go to any page you are stopped..

dan@dan-MacBookPro:~/Documents$ ping
PING ( 56(84) bytes of data.
--- ping statistics ---
19 packets transmitted, 0 received, 100% packet loss, time 18143ms

If we go to we will magically be redirected to! The login page that failed to popup when the connection was established.

Even if we manually go to the URL your browser may get tripped up and timeout over and over again. While waiting for the browser why not curl that beezy?

A quick curl will yield content so WTF is going on Mr. Browser? You must be getting stuck somewhere

Lets save the contents of that page to a file (!! > weblogin.html) and update any links to be absolute paths so all the php files load properly when we open it locally (we dont care about getting images/stylesheets to load).

To the source!! On first glance it should be pretty obvious that an ajax call is happening and we just need to click on an accept link to verify ourselves after its successful.

And voila we click on the link and are able to use the trash connection for the rest of the train ride.

Turbolinks: Leaving the Abyss

Just trying to do things like

<a href=”#” onclick=”alert(1)”>ZZZ</a>

Expected behavior here?.. Scroll to top of the page right? Not if turbolinks is doing its thang.

From my previous write-ups on Turbolinks (1 2) I was pretty understanding about the inherent behavioural differences for all things links.

If I wasn’t aware of Turbolinks I’d be incredibly confused by this incredibly simple bit of code. This time I was lucky enough to spot the Turbolinks loarder bar attempting to do unnecessary work and knew what the issue was

On top of initial confusion the fact that something like href=”#” doesn’t work according to spec is pretty frustrating and difficult to understand.

If we really want a link what do we do

<button> is your friend. Include Bootstrap and link style that SOB if you really want that link feel.. a bit annoying but easy enough to add to a project and remember class="btn btn-link"

The reason that works is because by default Turbolinks is only looking for <a href> links pointing to the current domain (of course we can turn this off w/ data-no-turbolinks='true'

All this additional knowledge for such little gain is not worth it. I don’t care to remember or reason about how Turbolinks will be handling history or trying to optimize potential page loads. Unless I’m building a single page app that has the same feel across all browsers and devices I’m not interested in the additional complexities.

No disrespect or hating on the idea for Rails 5 to include turbolinks by default; it’s understandable that to remain relevant in the app academy days of development folks are looking to build single page apps that will become the next hot thing.

I am officially on the rails new my_app --skip-turbolinks 🚂 now