# Unpacking a JsonPacker-packed sample **[cryptax.medium.com/unpacking-a-jsonpacker-packed-sample-4038e12119f5](https://cryptax.medium.com/unpacking-a-jsonpacker-packed-sample-4038e12119f5)** @cryptax June 27, 2022 [@cryptax](https://cryptax.medium.com/?source=post_page-----4038e12119f5--------------------------------) Jun 27 5 min read With students of mine, we built a static unpacker (short of public name, I named it “JsonPacker” in APKiD). Unfortunately, the static unpacker doesn’t work for the sample and the students are in for a quick patch before their defense in a few days. Lol. I’m sure they hate me 😁 The sha256 of the sample is ``` 2877b27f1b6c7db466351618dda4f05d6a15e9a26028f3fc064fa144ec3a1850, and it dates ``` back to February 2022. ## Quickly spot the encrypted json filename in the code There are many classes, and obfuscated names, so at first it could be a bit disorientating to find the right spot. But I’ve unpacked such samples dozens of time: just search for the class with `attachBaseContext (which is a method found in classes which derive from` ``` Application and which is called at “the very beginning”). ``` Head to class CToKhLqQwJbTrQrKg :) In there, head to the object fields which get their initial value in the constructor. Spot the json file `hq.json .` ----- The encrypted payload is inside hq.json. I like to rename the field to something more meaningful :) ## Spot the place where the file is dynamically loaded For such samples, just look where `DexClassLoader is used. I like to use the detailed report` of [DroidLysis for that.](http://github.com/cryptax/droidlysis) DexClassLoader is used in a single place: ABeJgOnNtJpIcNgRxUkDwXcIwNyTzCyFxXhUsZsWxQuShDpLkUiRyWn ----- Go to that class, and search for `DexClassLoader, you find method` `rigidmiddle .` This method loads dynamically the decrypted payload stored on the filesystem in “filename” Now, work your way up to who calls this method, using cross-references: ``` tailcreek guessextra aerobicneutral attachBaseContext : the call to aeronicneutral is at the end of the image below. ``` Parts of code of attachBaseContext. There is lots of junk code. The payload filename is used 3 times in this screenshots: loadover, ceilingnice, aerobicneutral ## Spot where payload decryption occurs ``` loadover constructs the absolute path of the filename. ceilingnice decrypts the file aerobicneutral loads the decrypted file. ``` ----- In `ceilingnice, let s follow the calls to the decryption algorithm:` ``` allrather orcharddecide ``` Method `orcharddecide loads the asset:` First, the code retrieves an AssetManager. Then it opens the encrypted payload asset. The input stream is the encrypted payload, the output stream will be stored in the location of absolutepath Then it reads the asset, decrypts it (this happens inside `futureinherit ), unzips the result` and writes it to the output stream. This part of code (inside orcharddecide) decrypts the assets and unzips the result. ``` futureinherit calls ratherbanana . It takes an encrypted byte array as input and ``` returns a decrypted byte array. ## Understanding the preparation of the key ``` ratherbanana reads a fixed string (“Ianj” for this sample), and I assume it is the key. It ``` converts the string to a byte array, then converts it to an integer array in `nomineesign .` Still lots of junk code. Prepare the decryption key. ----- The code of `nomineesign is not very long but requires close attention to remove junk code` (but not too much: the loop initializing the `convertedkey table is not junk!), and de-` obfuscate code. The line with StrictMath.hypot is obfuscated. For example, the lign with `hypot is interesting:` ``` int cv = (int)StrictMath.hypot(this.timeone('d', 0x1E681L, convertedkey, index), 0.0); timeone actually returns the index-th byte of convertedkey, i.e convertedkey[index] . hypot computes square root of ( convertedkey[index]² + 0² ). As 0² = 0, the variable cv ``` will simply receive the value of `convertedkey[index] .` In the end, the algorithm boils down to this: ``` private void swap(int a, int b, int[] array) { int tmp = array[a]; array[a]=array[b]; array[b]=tmp; } private int[] convert_key(byte[] key) { int[] convertedkey = new int[0x100]; int i; for(i = 0; i < 256; ++i) { convertedkey[i] = i; // init } int j = 0; int k = 0; while(j < 0x100) { int cv = convertedkey[j]; k = (k+cv+key[j%key.length]+0x100) % 0x100; swap(j, k, convertedkey); // swap values ++j; } return convertedkey;} ## Decryption algorithm ``` The next step is to understand the decryption algorithm in itself. Actually, there is lots of junk code that can be removed. To start, I focus on where the encrypted input byte array is used. ``` decrypted[i] = this.motionavoid(Math.round(v0_6) ^ encrypted[i]); ``` ----- The method motionavoid is there just for obfuscation: it merely returns its argument. Also, obviously, we only have integers, so Math.round is useless. So, we have `decrypted[i] =` ``` v0_6 ^ encrypted[i]; . A few lines above, we have v0_6 : int v0_6 = ckey[(v15 + v0_5) % 0x100]; . A few lines above, we have v15 and v0_5 : int v15 = this.timeone('b', 5222L, ckey, HMoEsEkXySsLhTyCkZlChSoBfFlPk.counter); // ckey[counter]this.GfnxRHLRQuDY_713808 = this.KfYicpzIQMgk_598597 * 0x12FC3 + this.RMSmhfBNuxnA_506561 - 50009; // junkint v0_5 = this.timeone('z', 0x179161L, ckey, v14); // ckey[v14] ``` The method `timeone only uses the last two arguments: a table and an index, and returns` ``` table[index] value. Quite strangely, v15 uses a static integer that I have renamed counter . I search where this counter is used: ``` The first use basically increments the counter, making sure it remains below 0x100. Then, counter is put in v2 and swapped with another value (energyalmost is a method that performs byte swap). Finally, v15 gets the value of the ckey[counter] I work out that `int v15 = ckey[counter]; .` As for `v0_5, we have` `v0_5 = ckey[v14] and` `v14 is yet another static counter:` `int` ``` v14 = HMoEsEkXySsLhTyCkZlChSoBfFlPk.other_counter; . Same, I search where this ``` other counter is used, and it’s basically the same: an increment modulus 0x100, a swap and ``` ckey[other_counter] . That’s it! We have all elements to decrypt! The algorithm boils ``` down to this: ----- Simplified decryption method. For this sample, the initial key is Ianj . The encrypted byte array is the contents of hq.json. I added a length argument because actually in my code the hq.json is read into a bigger array, and we only need to decrypt up to the length of hq.json file. _Note: the code above uses static variables counter and other_counter, but actually it works_ _fine with local variables, and probably would be easier to read with local ones._ ## Decrypt the payload To the key + decryption algorithm, we just need to add something to read `hq.json and` write to another file. Then, we can unpack! Static unpacker works fine :) Hurray! The decrypted file is a Zip file (this was expected: remember that `orcharddecide unzips` the result): inside, there is a `classes.dex` (sha256: ``` dae52bbee7f709fae9d91e06229c35b46d4559677f26152d4327fc1601d181be ). It is the ``` payload of the Xenomorph malware. ## Which class/method does the malware load dynamically? Before we decompile this payload, we need to know which method is called. The manifest shows the main activity is `com.sniff.sibling.MainActivity . This class is not present in` the wrapping apk… so it must be in the payload! This will be automatically called by Android as soon as it’s time to launch the main activity. The main activity is indeed found in the payload ----- We ve had enough for a single blog post, but the payload, similarly to many Android botnets, uses the Accessibility Services API to overlay windows of given applications. — Cryptax -----