Python scripting in dirtyJOE can be used to modify CONSTANT_Utf8 objects from Constant Pool. One of the main use is decryption of CONSTANT_Utf8 objects in obfuscated .class files. Decrypted objects are useful in forensic analysis, they can be also used to translate obfuscated java application. PyJOE library
In \scripts\ directory you can find pyjoe.py file which contains few useful functions that can be used by other scripts: def dj_normalizeUtf8(inBuffer):Function responsible for translating the Java Utf8 input buffer (tuple) to list of integer that represents each character. def dj_encodeUtf8(inBuffer):Function responsible for translating list of integers that represent each characters to the Java Utf8 buffer (list). def dj_decryptUTF8_ZKM(inBuffer, key):Standard decrypter for strings encoded by Zelix KlassMaster obfuscator, it can be also used to re-encrypt strings (very useful for translation of obfuscated Java applications). Usage example can be found in \scripts\zkm_decrypt.py file. def dj_decryptUTF8_Allatori(inBuffer, key):Standard decrypter for strings encoded by Allatori obfuscator, it can be also used to re-encrypt strings (very useful for translation of obfuscated Java applications). Usage example can be found in \scripts\allatori_decrypt.py file. ExampleI'll show how to use python scripting on sample obfuscated java malware called Boonana. Mentioned malware appeared in october 2010 and was obfuscated with Zelix KlassMaster 5.3.3E (according to entry in constant pool "ZKM5.3.3E"). Script should contain only one simple function that will be called by dirtyJOE on each encrypted Utf8 object: def dj_decryptUTF8(inBuf): return []
inBuf - input buffer, it is tuple, where each byte of Utf8 string is represented as integer value Universal script for decrypting Utf8 objects encrypted by Zelix KlassMaster obfuscator can be found in \scripts\ directory. It is very simple, all what is needed is to find proper 'key' value: def dj_decryptUTF8_ZKM(inBuffer, key): i = 0 ik = 0 ret = [] last = len(inBuffer) while i < last: c = inBuffer[i] if c == 0: break defAdd = 1 if (c >> 5) == 6: c = ((c & 0x1f) << 6) + (inBuffer[i+1] & 0x3f) defAdd = 2 elif (c >> 4) == 0xE: c = ((c & 0xf) << 12) + ((inBuffer[i+1] & 0x3f) << 6) \ + (inBuffer[i+2] & 0x3f) defAdd = 3 ret += [c ^ key[ik % 5]] i += defAdd ik += 1 return ret # 'instr' argument is input buffer represented as tuple # function should return list object def dj_decryptUTF8(inBuf): key = [48, 16, 127, 16, 97] return dj_decryptUTF8_ZKM(inBuf, key)
'key' value is very easy to find, all references to the encrypted object have to be checked, following those
references will reveal function that is responsible for decryption. For example:
00000086 : dup 00000087 : bipush 15 00000089 : ldc "Xd `[?gxdB~dOS" 0000008B : jsr pos.000000B0 0000008E : aastorejsr opcode is acronym for 'jump subroutine', and subroutine at pos.000000B0 looks like this: 000000B0 : astore_0 000000B1 : invokevirtual char[] java.lang.String.toCharArray() 000000B4 : dup 000000B5 : arraylength 000000B6 : swap 000000B7 : iconst_0 000000B8 : istore_1 000000B9 : swap 000000BA : dup_x1 000000BB : iconst_1 000000BC : if_icmpgt pos.0000010A 000000BF : dup 000000C0 : iload_1 000000C1 : dup2 000000C2 : caload 000000C3 : iload_1 000000C4 : iconst_5 000000C5 : irem 000000C6 : tableswitch l: 0, h: 3, def: pos.000000F8, pos.(000000E4, 000000E9, 000000EE, 000000F3) 000000E4 : bipush 48 000000E6 : goto pos.000000FA 000000E9 : bipush 16 000000EB : goto pos.000000FA 000000EE : bipush 127 000000F0 : goto pos.000000FA 000000F3 : bipush 16 000000F5 : goto pos.000000FA 000000F8 : bipush 97 000000FA : ixor 000000FB : i2c 000000FC : castore 000000FD : iinc local.01, 1 00000100 : swap 00000101 : dup_x1 00000102 : ifne pos.0000010A 00000105 : dup2 00000106 : swap 00000107 : goto pos.000000C1 0000010A : swap 0000010B : dup_x1 0000010C : iload_1 0000010D : if_icmpgt pos.000000BF 00000110 : new java.lang.String 00000113 : dup_x1 00000114 : swap 00000115 : invokespecial void java.lang.String.As it may be noticed, the green part of this subroutine contains key for decryption routine: 48, 16, 127, 16, 97. At this point, script can be tested in dirtyJOE by selecting 'Run Python Script' option from the Constant Pool context menu: 'Decrypt' button is used strictly for script testing purposes, it will show string after decryption in 'Preview' field. After clicking 'Save' button, object will be decrypted again, and user will be prompted by below message box: When script is finished it can be run on all encrypted Utf8 objects (by choosing 'Run Python Script on All Utf8 Objects' option from the Constant Pool context menu): 'Decrypt' button has similar function as in previous window. After clicking 'Save' button user will be prompted to accept all changes: That's pretty much all. |