rednaga / APKiD

Android Application Identifier for Packers, Protectors, Obfuscators and Oddities - PEiD for Android
Other
1.98k stars 290 forks source link

Allatori 6.0 obfuscation #92

Open pr3wtd opened 6 years ago

pr3wtd commented 6 years ago

Allatori obfuscation including custom class renaming

Sample file: f9f659da6cad4edbef46e452c0e2dfce0f124fd64662e22c30be70daaef1c44d

Reference: https://www.virustotal.com/#/file/f9f659da6cad4edbef46e452c0e2dfce0f124fd64662e22c30be70daaef1c44d/community

enovella commented 6 years ago

Hi @pr3wtd,

is this the sample we talked yesterday about? Do you have another samples which are 100% sure obfuscated with Allatori?

Thanks for opening an issue mate.

enovella commented 6 years ago

Also, can you upload the sample in this post or at Koodoous?

Cheers

pr3wtd commented 6 years ago

Hi @enovella,

Correct, that's the one. I couldn't attach the file in this post, it can be downloaded from: https://www74.zippyshare.com/v/cg3GOrqh/file.html (password: infected)

I've seen a couple of APKs with this kind of obfuscation. I will let you know as soon as I come accross some of them.

enovella commented 6 years ago

Hi @pr3wtd,

I cannot download the file, could you attach it here with extension zip?

Best

enovella commented 6 years ago

255843d5bf15d1a3fac6c19cc34300d1.zip

enovella commented 6 years ago

Hi @pr3wtd,

Can you provide more samples? It seems we can target the 12-char long classes, methods and fields all of them built with the regex [iI]{12}. I do need more samples to tweak the rule.

package nul;

import java.util.zip.*;
import com.google.gson.*;

public final class iIiiiIiiIiiI implements IIIIIiiIIIiI
{
    private static final /* synthetic */ byte[] IiiiiiiIiIII;
    public static final iiiiIIiIIIIi iIiIiiiIIIii;
    private /* synthetic */ iIiIIIiIIIiI IiIIiiiiIIiI;
    private /* synthetic */ byte[] IIiiIiiIIiII;
    private /* synthetic */ iIiIIIiIIIiI IIIIIiiiiiii;
    private /* synthetic */ IIIIiIiiiiiI IiiIIiiIiIiI;
    private /* synthetic */ iIiIIIiIIIiI IIiIiiiiIiII;

    public iiiiIIiIIIIi do() {
        int n;
        iIiiiIiiIiiI iIiiiIiiIiiI;
        if (this.IIIIIiiiiiii != null) {
            n = 8;
            iIiiiIiiIiiI = this;
        }
        else {
            n = 0;
            iIiiiIiiIiiI = this;
        }
        return new iiiiIIiIIIIi(n + ((iIiiiIiiIiiI.IIiIiiiiIiII != null) ? 8 : 0) + ((this.IiIIiiiiIIiI != null) ? 8 : 0) + ((this.IiiIIiiIiIiI != null) ? 4 : 0));
    }

    public iiiiIIiIIIIi new() {
        return new iiiiIIiIIIIi((this.IIIIIiiiiiii != null) ? 16 : 0);
    }

    public void new(final byte[] this, int this, int this) throws ZipException {
        if (this == 0) {
            return;
        }
        if (this < 16) {
            throw new ZipException("Zip64 extended information must contain both size values in the local file header.");
        }
        this.IIIIIiiiiiii = new iIiIIIiIIIiI(this, this);
        this += 8;
        this.IIiIiiiiIiII = new iIiIIIiIIIiI(this, this);
        final int n = this - 16;
        this += 8;
        if ((this = n) >= 8) {
            final int n2 = this;
            this += 8;
            this.IiIIiiiiIIiI = new iIiIIIiIIIiI(this, n2);
            this -= 8;
        }
        if (this >= 4) {
            final int n3 = this;
            this += 4;
            this.IiiIIiiIiIiI = new IIIIiIiiiiiI(this, n3);
            this -= 4;
        }
    }

    public iIiIIIiIIIiI do() {
        return this.IiIIiiiiIIiI;
    }

    public void do(final iIiIIIiIIIiI this) {
        this.IiIIiiiiIIiI = this;
    }

    public IIIIiIiiiiiI new() {
        return this.IiiIIiiIiIiI;
    }

    public byte[] new() {
        final byte[] array;
        int new1 = this.new(array = new byte[this.do().new()]);
        if (this.IiIIiiiiIIiI != null) {
            final byte[] new2 = this.IiIIiiiiIIiI.new();
            final int n = 0;
            final byte[] array2 = array;
            final int n2 = new1;
            new1 += 8;
            System.arraycopy(new2, n, array2, n2, 8);
        }
        if (this.IiiIIiiIiIiI != null) {
            final byte[] new3 = this.IiiIIiiIiIiI.new();
            final int n3 = 0;
            final byte[] array3 = array;
            final int n4 = new1;
            new1 += 4;
            System.arraycopy(new3, n3, array3, n4, 4);
        }
        return array;
    }

    public byte[] enum() {
        if (this.IIIIIiiiiiii == null && this.IIiIiiiiIiII == null) {
            return iIiiiIiiIiiI.IiiiiiiIiIII;
        }
        if (this.IIIIIiiiiiii == null || this.IIiIiiiiIiII == null) {
            throw new IllegalArgumentException("Zip64 extended information must contain both size values in the local file header.");
        }
        final byte[] this2 = new byte[16];
        this.new(this2);
        return this2;
    }

    public void enum(final iIiIIIiIIIiI this) {
        this.IIiIiiiiIiII = this;
    }

    public void new(final IIIIiIiiiiiI this) {
        this.IiiIIiiIiIiI = this;
    }

    public void new(final boolean this, final boolean this, final boolean this, final boolean this) throws ZipException {
        if (this.IIiiIiiIIiII != null) {
            int n;
            boolean b;
            if (this) {
                n = 8;
                b = this;
            }
            else {
                n = 0;
                b = this;
            }
            final int n2 = n + (b ? 8 : 0) + (this ? 8 : 0) + (this ? 4 : 0);
            if (this.IIiiIiiIIiII.length < n2) {
                throw new ZipException(new StringBuilder().insert(0, GsonBuilder.new("\u000e]\u0003L\u001fY\u0001\u0018\tQ\u001f]\u000eL\u0002J\u0014\u0018\u0017Q\u001d\u000eY\u0018\b@\u0019]\u0003\\\b\\MQ\u0003^\u0002J\u0000Y\u0019Q\u0002VM]\u0015L\u001fYM^\u0004]\u0001\\JKMT\bV\nL\u0005\u0018\tW\bK\u0003\u001f\u0019\u0018\u0000Y\u0019[\u0005\u0018\u000e]\u0003L\u001fY\u0001\u0018\tQ\u001f]\u000eL\u0002J\u0014\u0018\tY\u0019YC\u0018M}\u0015H\b[\u0019]\t\u0018\u0001]\u0003_\u0019PM")).append(n2).append(IiIIiIiIiiii.new("\u001fKJ]\u001f@L\t")).append(this.IIiiIiiIIiII.length).toString());
            }
            int n3 = 0;
            if (this) {
                final byte[] iIiiIiiIIiII = this.IIiiIiiIIiII;
                final int n4 = n3;
                n3 += 8;
                this.IIIIIiiiiiii = new iIiIIIiIIIiI(iIiiIiiIIiII, n4);
            }
            if (this) {
                final byte[] iIiiIiiIIiII2 = this.IIiiIiiIIiII;
                final int n5 = n3;
                n3 += 8;
                this.IIiIiiiiIiII = new iIiIIIiIIIiI(iIiiIiiIIiII2, n5);
            }
            if (this) {
                final byte[] iIiiIiiIIiII3 = this.IIiiIiiIIiII;
                final int n6 = n3;
                n3 += 8;
                this.IiIIiiiiIIiI = new iIiIIIiIIIiI(iIiiIiiIIiII3, n6);
            }
            if (this) {
                final byte[] iIiiIiiIIiII4 = this.IIiiIiiIIiII;
                final int n7 = n3;
                n3 += 4;
                this.IiiIIiiIiIiI = new IIIIiIiiiiiI(iIiiIiiIIiII4, n7);
            }
        }
    }

    public iIiiiIiiIiiI(final iIiIIIiIIIiI this, final iIiIIIiIIIiI this) {
        final IIIIiIiiiiiI iiiIiIiiiiiI = null;
        this(this, this, (iIiIIIiIIIiI)iiiIiIiiiiiI, iiiIiIiiiiiI);
    }

    public iiiiIIiIIIIi enum() {
        return iIiiiIiiIiiI.iIiIiiiIIIii;
    }

    public iIiIIIiIIIiI enum() {
        return this.IIIIIiiiiiii;
    }

    public iIiIIIiIIIiI new() {
        return this.IIiIiiiiIiII;
    }

    public void new(final iIiIIIiIIIiI this) {
        this.IIIIIiiiiiii = this;
    }

    static {
        iIiIiiiIIIii = new iiiiIIiIIIIi(1);
        IiiiiiiIiIII = new byte[0];
    }

    public void enum(final byte[] this, int this, final int this) throws ZipException {
        final int n = this;
        this.IIiiIiiIIiII = new byte[this];
        System.arraycopy(this, n, this.IIiiIiiIIiII, 0, this);
        if (this >= 28) {
            this.new(this, this, this);
            return;
        }
        if (this == 24) {
            this.IIIIIiiiiiii = new iIiIIIiIIIiI(this, this);
            this += 8;
            this.IIiIiiiiIiII = new iIiIIIiIIIiI(this, this);
            this += 8;
            this.IiIIiiiiIIiI = new iIiIIIiIIIiI(this, this);
            return;
        }
        if (this % 8 == 4) {
            this.IiiIIiiIiIiI = new IIIIiIiiiiiI(this, this + this - 4);
        }
    }

    public iIiiiIiiIiiI() {
    }

    private /* synthetic */ int new(final byte[] this) {
        int n = 0;
        if (this.IIIIIiiiiiii != null) {
            final byte[] new1 = this.IIIIIiiiiiii.new();
            final int n2 = 0;
            n += 8;
            System.arraycopy(new1, n2, this, n2, 8);
        }
        if (this.IIiIiiiiIiII != null) {
            final byte[] new2 = this.IIiIiiiiIiII.new();
            final int n3 = 0;
            final int n4 = n;
            n += 8;
            System.arraycopy(new2, n3, this, n4, 8);
        }
        return n;
    }

    public iIiiiIiiIiiI(final iIiIIIiIIIiI this, final iIiIIIiIIIiI this, final iIiIIIiIIIiI this, final IIIIiIiiiiiI this) {
        this.IIIIIiiiiiii = this;
        this.IIiIiiiiIiII = this;
        this.IiIIiiiiIIiI = this;
        this.IiiIIiiIiIiI = this;
    }
}
pr3wtd commented 5 years ago

Hey @enovella,

These samples look similarily obfuscated: 4991017aabfbeea83556691a2d23183d 9dbcbe543e7cbaaf751e7eca3dd60bb9

enovella commented 5 years ago

Hi @pr3wtd,

I cannot find this sample 4991017aabfbeea83556691a2d23183d. Can you upload it here? Please dont use external file sharing websites. Just Github servers.

Also, Koodous is very unstable these days, and very often the access to APKs is somehow limited.

pr3wtd commented 5 years ago

Here you go. 4991017aabfbeea83556691a2d23183d.zip

enovella commented 5 years ago

It seems that this sample 9dbcbe543e7cbaaf751e7eca3dd60bb9 is also not available at Koodous.

Just opened the one you shared over here. Some questions:

Heavy use of reflection allatori-v6

enovella commented 5 years ago

No answers? Otherwise we will close the issue.

pr3wtd commented 5 years ago

Sorry, currenly I have totally no time to pursue this issue. I will move it on in the future and put a reference on this particular issue.

CalebFenton commented 5 years ago

I'm going to keep this open since:

It's unfortunate we don't know more details about what exactly it is, and it would be nice to have those details, but we could still always create a rule to detect this, call it whatever, and link to this issue in the rules if people have more info.

I also like having these types of issues because they're an easy way for someone to contribute to the project.

enovella commented 5 years ago

Found an APK with this: (already obfuscated with DexProtector)

package com.microblink.secured;

import com.microblink.util.Log;

public class IIlIIlIIII {
    private static Error llIIlIlIIl;
    private static boolean llIIlllIll;

    static {
    }

    public IIlIIlIIII() {
        super();
    }

    public static void IlIIlIIIII() {
        if(!IIlIIlIIII.IlIIlllIIl()) {
            Error v0 = IIlIIlIIII.llIIlIlIIl;
            if(v0 != null) {
                throw v0;
            }

            throw new RuntimeException("Native library is not loaded");
        }
    }

    public static boolean IlIIlllIIl() {
        if(!IIlIIlIIII.llIIlllIll) {
            try {
                if(lIlIIIIlIl.llIIlIIlll()) {
                    String[] v1_1 = llIIlIlIIl.llIIlIlIIl;
                    int v2 = v1_1.length;
                    int v3;
                    for(v3 = 0; v3 < v2; ++v3) {
                        String v5 = v1_1[v3];
                        Log.d(IIlIIlIIII.class, "Loading lib{}.so", new Object[]{v5});
                        System.loadLibrary(v5);
                    }

                    IIlIIlIIII.llIIlllIll = true;
                    goto label_32;
                }

                throw new UnsatisfiedLinkError("Incompatible CPU!");
            }
            catch(Error v1) {
                IIlIIlIIII.llIIlllIll = false;
                Log.e(IIlIIlIIII.class, ((Throwable)v1), "error loading native library", new Object[0]);
                IIlIIlIIII.llIIlIlIIl = v1;
            }
        }

    label_32:
        return IIlIIlIIII.llIIlllIll;
    }

    public static Error llIIlIlIIl() {
        return IIlIIlIIII.llIIlIlIIl;
    }

    public static boolean llIllIIlll() {
        return IIlIIlIIII.llIIlllIll;
    }
}

sample: b2f94d7521326fbf01e9749acc3033b3881d97457249eaadd48567a26d11087a

allatori

eybisi commented 4 years ago

Hi, suprised to see that apkid can't detect allatori. Here is what I know so far about allatori. There are 2 version of it. Demo and commercial (?) one. Demo version only obfuscates strings ( uses mostly static class name : ALLATORIxDEMO for decryption )

Demo Sample allatori_demo

In demo version strings are obfuscated with xor loops that have 2 key. No stack trace is used. There are multiple xor functions. demo_xor

Commercial version use IiIIIIIIii like strings as a class name and make use of stack trace in obfuscation. Sample allatori_com

Samples of this variant

enovella commented 4 years ago

APKiD can detect Allatori demo version:

> apkid allatoridemo_d810d0059234f4fb2fe4084a3021c2bf94e986011bb2299d1e132e6b3b46e486.apk 
[+] APKiD 2.1.0 :: from RedNaga :: rednaga.io
[*] allatoridemo_d810d0059234f4fb2fe4084a3021c2bf94e986011bb2299d1e132e6b3b46e486.apk!classes.dex
 |-> anti_debug : Debug.isDebuggerConnected() check
 |-> anti_vm : Build.MANUFACTURER check, Build.MODEL check, Build.PRODUCT check, Build.TAGS check
 |-> compiler : dx
 |-> obfuscator : Allatori demo

The commercial version of Allatori has not been implemented yet because the lack of certainty and verified samples.

>  apkid allatori_c0dd3437035073e3ef0c421c5410b874f6940c8cd7c0d829fb3297c63fa70216.apk 
[+] APKiD 2.1.0 :: from RedNaga :: rednaga.io
[*] allatori_c0dd3437035073e3ef0c421c5410b874f6940c8cd7c0d829fb3297c63fa70216.apk!classes.dex
 |-> anti_debug : Debug.isDebuggerConnected() check
 |-> anti_vm : Build.MANUFACTURER check, Build.MODEL check, Build.PRODUCT check, Build.TAGS check, SIM operator check, network operator name check, possible ro.secure check
 |-> compiler : dx

Are you willing to pull-request it?

enovella commented 4 years ago

Using only the string IiIIIIIIii may lead to false positives. Could you please elaborate a bit more about how to create a solid rule for detecting the commercial version of Allatori?

enovella commented 4 years ago

It seems that this sample 9dbcbe543e7cbaaf751e7eca3dd60bb9 is also not available at Koodous.

Just opened the one you shared over here. Some questions:

  • How sure are you that this is Allatori? If so, why?
  • Is this malware right? Is Allatori heavily using reflection? or this is part of the malware?
  • The package name of Lcom/android seems to be suspicious. Wondering if this obfuscation is customized for malware only or it is a real obfuscator, which has been used for malware purposes.
  • Can you provide ideas on how to fingerprint it?
  • Anything unique of these obfuscator/malware samples?

Heavy use of reflection allatori-v6

@eybisi could you also verify this sample? Are you sure that's protected with Allatori?

enovella commented 4 years ago

Found an APK with this: (already obfuscated with DexProtector)

package com.microblink.secured;

import com.microblink.util.Log;

public class IIlIIlIIII {
    private static Error llIIlIlIIl;
    private static boolean llIIlllIll;

    static {
    }

    public IIlIIlIIII() {
        super();
    }

    public static void IlIIlIIIII() {
        if(!IIlIIlIIII.IlIIlllIIl()) {
            Error v0 = IIlIIlIIII.llIIlIlIIl;
            if(v0 != null) {
                throw v0;
            }

            throw new RuntimeException("Native library is not loaded");
        }
    }

    public static boolean IlIIlllIIl() {
        if(!IIlIIlIIII.llIIlllIll) {
            try {
                if(lIlIIIIlIl.llIIlIIlll()) {
                    String[] v1_1 = llIIlIlIIl.llIIlIlIIl;
                    int v2 = v1_1.length;
                    int v3;
                    for(v3 = 0; v3 < v2; ++v3) {
                        String v5 = v1_1[v3];
                        Log.d(IIlIIlIIII.class, "Loading lib{}.so", new Object[]{v5});
                        System.loadLibrary(v5);
                    }

                    IIlIIlIIII.llIIlllIll = true;
                    goto label_32;
                }

                throw new UnsatisfiedLinkError("Incompatible CPU!");
            }
            catch(Error v1) {
                IIlIIlIIII.llIIlllIll = false;
                Log.e(IIlIIlIIII.class, ((Throwable)v1), "error loading native library", new Object[0]);
                IIlIIlIIII.llIIlIlIIl = v1;
            }
        }

    label_32:
        return IIlIIlIIII.llIIlllIll;
    }

    public static Error llIIlIlIIl() {
        return IIlIIlIIII.llIIlIlIIl;
    }

    public static boolean llIllIIlll() {
        return IIlIIlIIII.llIIlllIll;
    }
}

sample: b2f94d7521326fbf01e9749acc3033b3881d97457249eaadd48567a26d11087a

allatori

@eybisi could you also verify this sample? Are you sure that's protected with Allatori?

enovella commented 4 years ago

Too many false positives!

rule allatori : obfuscator
{
  meta:
    description = "Allatori"
    url         = "http://www.allatori.com/features.html"
    author      = "Eduardo Novella"
    sample      = "c0dd3437035073e3ef0c421c5410b874f6940c8cd7c0d829fb3297c63fa70216"

  strings:
    // null-prev-str + len + str + null
    $method_7  = { 00 07 (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) 00}
    $method_8  = { 00 08 (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) 00}
    $method_10 = { 00 0A (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) (49|69|6a|6c|4a|4c) 00}

    // $method = /[iIlLjJ]{7,12}/

  condition:
    (#method_7 > 10 or #method_8 > 10 or #method_10 > 10) and is_dex
    // #method > 10 and is_dex and not allatori_demo
}
eybisi commented 4 years ago

I don't think these are allatori. For string encryption allatori use basic xor or make use of stack trace. I didn't see any reflection+string encryption in malware samples for allatori.

Also I saw demo samples without ALLATORIxDEMO string instead random function names. Couldn't find them right now, but I'm pretty sure. Because I used this script to decrypt strings and I was changing function name day by day.

If we use string encryption function as base point for detection, I propose following dalvik opcodes for fingerprinting. At least we can identify allatori samples with string encryption is on. comp


rule allatori_demo
{
    strings:
    /*
        char[] cArr = new char[length];
        int i = length - 1;
        while (i >= 0) {
            int i2 = i - 1;
            cArr[i] = (char) (str.charAt(i) ^ 'T');
            if (i2 < 0) {
                break;
            }
            i = i2 - 1;
            cArr[i2] = (char) (str.charAt(i2) ^ '9');
        }
    */

    $twokeyxor = {
        0a 00               //   move-result v0
        23 01 ?? ??         //   new-array v1, v0, [C
        d8 00 00 ff         //   add-int/lit8 v0, v0, -0x1
        3a 00 1b 00         //   if-ltz v0, :cond_0
        6e 20 ?? ?? 04 00   //   invoke-virtual {p0, v0}, Ljava/lang/String;->charAt(I)C
        0a 02               //   move-result v2
        d8 03 00 ff         //   add-int/lit8 v3, v0, -0x1
        df 02 02 ??         //   xor-int/lit8 v2, v2, 0x54
        8e 22               //   int-to-char v2, v2
        50 02 01 00         //   aput-char v2, v1, v0
        3a 03 0e 00         //   if-ltz v3, :cond_0
        d8 00 03 ff         //   add-int/lit8 v0, v3, -0x1
        6e 20 ?? ?? 34 00   //   invoke-virtual {p0, v3}, Ljava/lang/String;->charAt(I)C
        0a 02               //   move-result v2
        df 02 02 ??         //   xor-int/lit8 v2, v2, 0x39
        8e 22               //   int-to-char v2, v2
        50 02 01 03         //   aput-char v2, v1, v3
        28 e6               //   goto 
    }

    condition:
        $is_dex and $twokeyxor

}

Same idea for commercial rule.


rule allatori_commercial
{
    strings:
    /*
        while (i >= 0) {
            int i4 = i3 - 1;
            cArr[i3] = (char) ((str.charAt(i3) ^ stringBuffer.charAt(i2)) ^ 11);
            if (i4 < 0) {
                break;
            }
            i3 = i4 - 1;
            int i5 = i2 - 1;
            cArr[i4] = (char) ((str.charAt(i4) ^ stringBuffer.charAt(i2)) ^ 'N');
            if (i5 < 0) {
                i5 = length;
            }
            i2 = i5;
            i = i3;
        }
        return new String(cArr);
    */

    $stacktracexor = {
        0a 00               //   move-result v0
        23 05 ?? ??         //   new-array v5, v0, [C
        d8 00 00 ff         //   add-int/lit8 v0, v0, -0x1
        01 13               //   move v3, v1
        01 02               //   move v2, v0
        3b 00 08 00         //   if-gez v0, :cond_1
        22 00 ?? ??         //   new-instance v0, Ljava/lang/String;
        70 20 ?? ?? 50 00   //   invoke-direct {v0, v5}, Ljava/lang/String;-><init>([C)V
        11 00               //   return-object v0
        d8 06 02 ff         //   add-int/lit8 v6, v2, -0x1
        6e 20 ?? ?? 28 00   //   invoke-virtual {p0, v2}, Ljava/lang/String;->charAt(I)C
        0a 00               //   move-result v0
        6e 20 ?? ?? 34 00   //   invoke-virtual {v4, v3}, Ljava/lang/String;->charAt(I)C
        0a 07               //   move-result v7
        b7 70               //   xor-int/2addr v0, v7
        df 00 00 ??         //   xor-int/lit8 v0, v0, 0x35
        8e 00               //   int-to-char v0, v0
        50 00 05 02         //   aput-char v0, v5, v2
        3a 06 ea ff         //   if-ltz v6,
        6e 20 ?? ?? 68 00   //   invoke-virtual {p0, v6}, Ljava/lang/String;->charAt(I)C
        0a 00               //   move-result v0
        6e 20 ?? ?? 34 00   //   invoke-virtual {v4, v3}, Ljava/lang/String;->charAt(I)C
        0a 02               //   move-result v2
        b7 20               //   xor-int/2addr v0, v2
        df 00 00 ??         //   xor-int/lit8 v0, v0, 0x6
        8e 07               //   int-to-char v7, v0
        d8 02 06 ff         //   add-int/lit8 v2, v6, -0x1
        d8 00 03 ff         //   add-int/lit8 v0, v3, -0x1
        50 07 05 06         //   aput-char v7, v5, v6
        3b 00 03 00         //   if-gez v0,
        01 10               //   move v0, v1
    }

    condition:
        $is_dex and $stacktracexor

}
enovella commented 4 years ago

Have you run these two rules against a bunch apks to detect false positives? The ideal rule should contain something unique to this protector. Open a PR, and I will test it.