Unpacking a JsonPacker-packed sample

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 :)
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.

DexClassLoader is used in a single place: ABeJgOnNtJpIcNgRxUkDwXcIwNyTzCyFxXhUsZsWxQuShDpLkUiRyWn
This method loads dynamically the decrypted payload stored on the filesystem in “filename”
  • 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.
  • allrather
  • orcharddecide
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
This part of code (inside orcharddecide) decrypts the assets and unzips the result.

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 line with StrictMath.hypot is obfuscated.
int cv = (int)StrictMath.hypot(this.timeone('d', 0x1E681L, convertedkey, index), 0.0);
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]);
int v15 = this.timeone('b', 5222L, ckey, HMoEsEkXySsLhTyCkZlChSoBfFlPk.counter);  // ckey[counter]
this.GfnxRHLRQuDY_713808 = this.KfYicpzIQMgk_598597 * 0x12FC3 + this.RMSmhfBNuxnA_506561 - 50009; // junk
int v0_5 = this.timeone('z', 0x179161L, ckey, v14); // ckey[v14]
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]
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.

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!

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.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
@cryptax

@cryptax

Mobile and IoT malware researcher. The postings on this account are solely my own opinion and do not represent my employer.