Just An Application

September 6, 2014

The Mystery Of The Unsigned JAR: Part Two — The Sekrit Method

The ‘sekrit method’ takes a single String argument and returns a String and it could not be more obvious if it tried.

Unlike the other methods in the class it has a non-meaningful name. Instead its name features a large subset of the lower case letters of the alphabet in order. I don’t think there is any coding standard that requires that, ‘tho I suppose there could be.

It is always passed a String literal and the String literal is always gibberish, yet the return values are often passed to factory methods in the Java API which take specific well-formed names.

The inference is obvious. The method takes obfuscated Strings and turns them into unobfuscated Strings.

The nice thing about Java VM bytecode is that you can decompile it using nothing more than a pencil and a piece of paper, oh, and a knowledge of the Java VM opcodes !

Being stack based it is pretty easy to work out what is going on by tracing the state of the stack and the local variables after each instruction.

Of course if you don’t have a piece of paper and a pencil handy you can always write some code to do the same thing.

The following shows the stack/local variable state after each instruction for a large part of the sekrit method.

The top of the stack is on the right.

    ...

    
    34: invokevirtual #232                // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
    
        S
    
    37: dup
    
        S, S
    
    38: invokevirtual #19                 // Method java/lang/String.length:()I
    
        S, length(S)
    
    41: iconst_1
    
        S, length(S), 0x01
    
    42: isub
    
        S, length(S)_minus_one
    
    43: iconst_2
    
        S, length(S)_minus_one, 0x02
    
    44: iconst_3
    
        S, length(S)_minus_one, 0x02, 0x03
    
    45: ishl
    
        S, length(S)_minus_one, 0x10
    
    46: iconst_3
    
        S, length(S)_minus_one, 0x10, 0x03
    
    47: iconst_5
    
        S, length(S)_minus_one, 0x10, 0x03, 0x05
    
    48: ixor
    
        S, length(S)_minus_one, 0x10, 0x06
    
    49: ixor
    
        S, length(S)_minus_one, 0x16
    
    50: iconst_2
    
        S, length(S)_minus_one, 0x16, 0x02
    
    51: iconst_5
    
        S, length(S)_minus_one, 0x16, 0x02, 0x05
    
    52: ixor
    
        S, length(S)_minus_one, 0x16, 0x07
    
    53: iconst_3
    
        S, length(S)_minus_one, 0x16, 0x07, 0x03
    
    54: ishl
    
        S, length(S)_minus_one, 0x16, 0x38
    
    55: iconst_2
    
        S, length(S)_minus_one, 0x16, 0x38, 0x02
    
    56: ixor
    
        S, length(S)_minus_one, 0x16, 0x3A
    
    57: iconst_2
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x02
    
    58: iconst_5
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x02, 0x05
    
    59: ixor
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x07
    
    60: iconst_4
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x07, 0x04
    
    61: ishl
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x70
    
    62: iconst_1
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x70, 0x01
    
    63: dup
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x70, 0x01, 0x01
    
    64: ishl
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x70, 0x02
    
    65: ixor
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x72
    
    66: aload_0
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x72, avar0
    
    67: invokevirtual #19                 // Method java/lang/String.length:()I
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x72, length(avar0)
    
    70: dup
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x72, length(avar0), length(avar0)
    
    71: newarray       char
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x72, length(avar0), char[length(avar0)]
    
    73: iconst_1
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x72, length(avar0), char[length(avar0)], 0x01
    
    74: dup
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x72, length(avar0), char[length(avar0)], 0x01, 0x01
    
    75: pop2
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x72, length(avar0), char[length(avar0)]
    
    76: swap
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x72, char[length(avar0)], length(avar0)
    
    77: iconst_1
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x72, char[length(avar0)], length(avar0), 0x01
    
    78: isub
    
        S, length(S)_minus_one, 0x16, 0x3A, 0x72, char[length(avar0)], length(avar0)_minus_1
    
    79: dup_x2
    
        S, length(S)_minus_one, 0x16, 0x3A, length(avar0)_minus_1, 0x72, char[length(avar0)], length(avar0)_minus_1
    
    80: istore_1
    
        S, length(S)_minus_one, 0x16, 0x3A, length(avar0)_minus_1, 0x72, char[length(avar0)]
    
        {
            ivar1 == length(avar0)_minus_1
        }
    
    81: astore_3
    
        S, length(S)_minus_one, 0x16, 0x3A, length(avar0)_minus_1, 0x72
    
        {
            ivar1 == length(avar0)_minus_1
            avar3 == char[length(avar0)]
        }
    
    
    82: istore        7
    
        S, length(S)_minus_one, 0x16, 0x3A, length(avar0)_minus_1
    
        {
            ivar1 == length(avar0)_minus_1
            avar3 == char[length(avar0)]
            ivar7 == 0x72
        }
    
    84: dup_x2
    
        S, length(S)_minus_one, length(avar0)_minus_1, 0x16, 0x3A, length(avar0)_minus_1
    
    85: pop
    
        S, length(S)_minus_one, length(avar0)_minus_1, 0x16, 0x3A
    
    86: istore        4
    
        S, length(S)_minus_one, length(avar0)_minus_1, 0x16
    
        {
            ivar1 == length(avar0)_minus_1
            avar3 == char[length(avar0)]
            ivar4 == 0X3A
            ivar7 == 0x72
        }
    
    88: pop
    
        S, length(S)_minus_one, length(avar0)_minus_1
    
    89: swap
    
        S, length(avar0)_minus_1, length(S)_minus_one
    
    90: dup
    
        S, length(avar0)_minus_1, length(S)_minus_one, length(S)_minus_one
    
    91: istore_2
    
        S, length(avar0)_minus_1, length(S)_minus_one
    
        {
            ivar1 == length(avar0)_minus_1
            ivar2 == length(S)_minus_one
            avar3 == char[length(avar0)]
            ivar4 == 0X3A
            ivar7 == 0x72
        }
    
    92: istore        5
    
        S, length(avar0)_minus_1
    
        {
            ivar1 == length(avar0)_minus_1
            ivar2 == length(S)_minus_one
            avar3 == char[length(avar0)]
            ivar4 == 0X3A
            ivar5 == length(S)_minus_one
            ivar7 == 0x72
    }
    
    94: swap
    
        length(avar0)_minus_1, S
    
    95: astore        6
    
        length(avar0)_minus_1
    
        {
            ivar1 == length(avar0)_minus_1
            ivar2 == length(S)_minus_one
            avar3 == char[length(avar0)]
            ivar4 == 0x3A
            ivar5 == length(S)_minus_one
            avar6 == S
            ivar7 == 0x72
        }
    
    97: goto          163
    
    
    100: aload_3
    
        char[length(avar0)]
    
    101: iload         4
    
        char[length(avar0)], 0x3A
    
    103: aload_0
    
        char[length(avar0)], 0x3A, avar0
    
    104: iload_1
    
        char[length(avar0)], 0x3A, avar0, ivar1
    
    105: iinc          1, -1
    
        char[length(avar0)], 0x3A, avar0, ivar1
    
    108: dup_x2
    
        char[length(avar0)], ivar1, 0x3A, avar0, ivar1
    
    109: invokevirtual #236                // Method java/lang/String.charAt:(I)C
    
        char[length(avar0)], ivar1, 0x3A, c
    
    112: aload         6
    
        char[length(avar0)], ivar1, 0x3A, c, avar6
    
    114: iload_2
    
        char[length(avar0)], ivar1, 0x3A, c, avar6, ivar2
    
    115: invokevirtual #236                // Method java/lang/String.charAt:(I)C
    
        char[length(avar0)], ivar1, 0x3A, c, d
    
    118: ixor
    
        char[length(avar0)], ivar1, 0x3A, c_xor_d
    
    119: ixor
    
        char[length(avar0)], ivar1, c_xor_d_xor_0x3A
    
    120: i2c
    
        char[length(avar0)], ivar1, c_xor_d_xor_0x3A
    
    121: castore
    
    
    122: iload_1
    
        ivar1
    
    123: ifge          130
    126: aload_3
    127: goto          167
    
    
    130: aload_3
    
        avar3
    
    131: iload         7
    
        avar3, 0x72
    
    133: aload_0
    
        avar3, 0x72, avar0
    
    134: iload_1
    
        avar3, 0x72, avar0, ivar1
    
    135: dup_x2
    
        avar3, ivar1, 0x72, avar0, ivar1
    
    136: invokevirtual #236                // Method java/lang/String.charAt:(I)C
    
        avar3, ivar1, 0x72, c
    
    139: aload         6
    
        avar3, ivar1, 0x72, c, avar6
    
    141: iload_2
    
        avar3, ivar1, 0x72, c, avar6, ivar2
    
    142: invokevirtual #236                // Method java/lang/String.charAt:(I)C
    
        avar3, ivar1, 0x72, c, d
        
    145: ixor
    
        avar3, ivar1, 0x72, c_xor_d
    
    146: ixor
    
        avar3, ivar1, c_xor_d_xor_0x72
    
    147: i2c
    
        avar3, ivar1, c_xor_d_xor_0x72
    
    148: iinc          1, -1
    151: iinc          2, -1
    154: castore
    
    
    155: iload_2
    
        ivar2
    
    156: ifge          162
    
    159: iload         5
    
        ivar5
        
    161: istore_2
    
    
    162: iload_1
    
        ivar1
        
    163: ifge          100
    
    
    166: aload_3
    
        avar3
        
    167: invokespecial #239                // Method java/lang/String."<init>":([C)V
        
        
    170: areturn

The method is basically is a loop which starts at

    100: aload_3

and ends at

    163: ifge          100

Everything before the loop is initialization code, some of which seems to have been partially obfuscated by the use of various bitwise operators acting on small integers.

Following the initialization we can deduce the following.

The local variable avar0 holds the obfuscated String passed as the argument.

The local variable avar3 holds an array of chars which will contain the unobfuscated characters.

The local variable ivar1 holds the index used to access the characters in avar0 and avar3. Note that it being decremented

    105: iinc          1, -1

and, compared against 0

    123: ifge          130

so both avar0 and avar3 are being iterated over backwards.

The local variable ivar2 is used in the same way to iterate backwards over the String in avar6.

Each time round the loop a character is added to avar3. The character is the result of XORing a character from avar0 and a character from avar6 and the constant 0x3A. [Instructions 100 to 121]

If there are still characters to be added a second character is added which is the result of XORing a character from avar0 and a character from avar6 and the constant 0x72. [Instructions 130 to 154]

Note that ivar1 is being decremented twice each time round the loop but ivar2 only once.

The String in avar6 is constructed using the name of the calling method so the obfuscation is context sensitive.

If you convert all of the above back into Java you will probably end up with something like this

        ...
    
        int     lengthA = a.length();
        int     lengthS = s.length();
        char[]  chars   = new char[lengthA];
        int     i       = lengthA - 1;
        int     j       = lengthS - 1;
    
        while (true)
        {
            char c = a.charAt(i);
            char d = s.charAt(j);
    
            chars[i] = (char)(c ^ d ^ 0x3a);
    
            --i;
            if (i < 0)
            {
                break;
            }
    
            c = a.charAt(i);
            d = s.charAt(j);
    
            chars[i] = (char)(c ^ d ^ 0x72);
    
            --i;
            --j;
            if (j < 0)
            {
                j = lengthS -1;
            }
            if (i < 0)
            {
                break;
            }
        }
        return new String(chars);

where a is the obfuscated String being passed as the argument and s is the context sensitive ‘mask’.

I doubt whether it compiles into exactly the same bytecode but it definitely deobfuscates the String literals in the class files correctly.


Copyright (c) 2014 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: