为了防止静态分析,ZMK在混淆时对输出的字符串使用对称加密方法进行加密
早期的ZKM只在静态代码块的时候进行简单的异或加密,后续版本使用了流加密技术进行二次加密
看一个简单的字符串输出例子
package com.vvvtimes.main;
public class Main {
public static void main(String[] args) {
System.out.println("Hello");
System.out.println("World");
}
}
混淆反编译之后的代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.vvvtimes.main;
public class a {
public static int a;
public static int b;
private static String[] c;
private static String[] d;
public a() {
}
public static void main(String[] var0) {
int var10000 = b;
System.out.println(a(-7991, 8444));
int var1 = var10000;
System.out.println(a(-7992, -25829));
if (var1 != 0) {
int var2 = a;
++var2;
a = var2;
}
}
static {
String[] var5 = new String[2];
int var3 = 0;
String var2 = "\u0093íè[Í\u0005ükçÀú";
int var4 = var2.length();
char var1 = 5;
int var0 = -1;
boolean flag =true;
while(flag) {
char[] var10002;
label41: {
++var0;
char[] var10001 = var2.substring(var0, var0 + var1).toCharArray();
int var10003 = var10001.length;
int var6 = 0;
var10002 = var10001;
int var7 = var10003;
char[] var9;
int var10004;
if (var10003 <= 1) {
var9 = var10001;
var10004 = var6;
} else {
var10002 = var10001;
var7 = var10003;
if (var10003 <= var6) {
break label41;
}
var9 = var10001;
var10004 = var6;
}
while(true) {
char var10005 = var9[var10004];
byte var10006;
switch(var6 % 7) {
case 0:
var10006 = 116;
break;
case 1:
var10006 = 91;
break;
case 2:
var10006 = 57;
break;
case 3:
var10006 = 40;
break;
case 4:
var10006 = 121;
break;
case 5:
var10006 = 92;
break;
default:
var10006 = 118;
}
var9[var10004] = (char)(var10005 ^ var10006);
++var6;
if (var7 == 0) {
var10004 = var7;
var9 = var10002;
} else {
if (var7 <= var6) {
break;
}
var9 = var10002;
var10004 = var6;
}
}
}
String var11 = (new String(var10002)).intern();
var5[var3++] = var11;
if ((var0 += var1) >= var4) {
c = var5;
d = new String[2];
flag=false;
}else{
var1 = var2.charAt(var0);
}
}
}
private static String a(int var0, int var1) {
int var2 = (var0 ^ -7991) & '\uffff';
if (d[var2] == null) {
char[] var3 = c[var2].toCharArray();
short var10000;
switch(var3[0] & 255) {
case 0:
var10000 = 181;
break;
case 1:
var10000 = 149;
break;
case 2:
var10000 = 85;
break;
case 3:
var10000 = 171;
break;
case 4:
var10000 = 128;
break;
case 5:
var10000 = 103;
break;
case 6:
var10000 = 238;
break;
case 7:
var10000 = 159;
break;
case 8:
var10000 = 225;
break;
case 9:
var10000 = 234;
break;
case 10:
var10000 = 14;
break;
case 11:
var10000 = 200;
break;
case 12:
var10000 = 138;
break;
case 13:
var10000 = 205;
break;
case 14:
var10000 = 127;
break;
case 15:
var10000 = 50;
break;
case 16:
var10000 = 186;
break;
case 17:
var10000 = 169;
break;
case 18:
var10000 = 68;
break;
case 19:
var10000 = 61;
break;
case 20:
var10000 = 10;
break;
case 21:
var10000 = 46;
break;
case 22:
var10000 = 121;
break;
case 23:
var10000 = 230;
break;
case 24:
var10000 = 80;
break;
case 25:
var10000 = 89;
break;
case 26:
var10000 = 0;
break;
case 27:
var10000 = 24;
break;
case 28:
var10000 = 167;
break;
case 29:
var10000 = 5;
break;
case 30:
var10000 = 132;
break;
case 31:
var10000 = 53;
break;
case 32:
var10000 = 81;
break;
case 33:
var10000 = 231;
break;
case 34:
var10000 = 141;
break;
case 35:
var10000 = 251;
break;
case 36:
var10000 = 241;
break;
case 37:
var10000 = 219;
break;
case 38:
var10000 = 173;
break;
case 39:
var10000 = 20;
break;
case 40:
var10000 = 38;
break;
case 41:
var10000 = 182;
break;
case 42:
var10000 = 229;
break;
case 43:
var10000 = 67;
break;
case 44:
var10000 = 183;
break;
case 45:
var10000 = 188;
break;
case 46:
var10000 = 222;
break;
case 47:
var10000 = 107;
break;
case 48:
var10000 = 248;
break;
case 49:
var10000 = 244;
break;
case 50:
var10000 = 156;
break;
case 51:
var10000 = 88;
break;
case 52:
var10000 = 246;
break;
case 53:
var10000 = 240;
break;
case 54:
var10000 = 13;
break;
case 55:
var10000 = 211;
break;
case 56:
var10000 = 49;
break;
case 57:
var10000 = 144;
break;
case 58:
var10000 = 40;
break;
case 59:
var10000 = 21;
break;
case 60:
var10000 = 130;
break;
case 61:
var10000 = 179;
break;
case 62:
var10000 = 202;
break;
case 63:
var10000 = 194;
break;
case 64:
var10000 = 201;
break;
case 65:
var10000 = 174;
break;
case 66:
var10000 = 117;
break;
case 67:
var10000 = 99;
break;
case 68:
var10000 = 137;
break;
case 69:
var10000 = 6;
break;
case 70:
var10000 = 12;
break;
case 71:
var10000 = 153;
break;
case 72:
var10000 = 213;
break;
case 73:
var10000 = 206;
break;
case 74:
var10000 = 93;
break;
case 75:
var10000 = 249;
break;
case 76:
var10000 = 33;
break;
case 77:
var10000 = 28;
break;
case 78:
var10000 = 120;
break;
case 79:
var10000 = 95;
break;
case 80:
var10000 = 37;
break;
case 81:
var10000 = 4;
break;
case 82:
var10000 = 55;
break;
case 83:
var10000 = 237;
break;
case 84:
var10000 = 102;
break;
case 85:
var10000 = 196;
break;
case 86:
var10000 = 34;
break;
case 87:
var10000 = 216;
break;
case 88:
var10000 = 143;
break;
case 89:
var10000 = 98;
break;
case 90:
var10000 = 133;
break;
case 91:
var10000 = 94;
break;
case 92:
var10000 = 203;
break;
case 93:
var10000 = 254;
break;
case 94:
var10000 = 92;
break;
case 95:
var10000 = 2;
break;
case 96:
var10000 = 16;
break;
case 97:
var10000 = 124;
break;
case 98:
var10000 = 48;
break;
case 99:
var10000 = 11;
break;
case 100:
var10000 = 3;
break;
case 101:
var10000 = 163;
break;
case 102:
var10000 = 221;
break;
case 103:
var10000 = 195;
break;
case 104:
var10000 = 192;
break;
case 105:
var10000 = 59;
break;
case 106:
var10000 = 119;
break;
case 107:
var10000 = 161;
break;
case 108:
var10000 = 72;
break;
case 109:
var10000 = 29;
break;
case 110:
var10000 = 160;
break;
case 111:
var10000 = 224;
break;
case 112:
var10000 = 198;
break;
case 113:
var10000 = 41;
break;
case 114:
var10000 = 42;
break;
case 115:
var10000 = 65;
break;
case 116:
var10000 = 114;
break;
case 117:
var10000 = 136;
break;
case 118:
var10000 = 176;
break;
case 119:
var10000 = 22;
break;
case 120:
var10000 = 122;
break;
case 121:
var10000 = 209;
break;
case 122:
var10000 = 129;
break;
case 123:
var10000 = 100;
break;
case 124:
var10000 = 112;
break;
case 125:
var10000 = 82;
break;
case 126:
var10000 = 43;
break;
case 127:
var10000 = 35;
break;
case 128:
var10000 = 83;
break;
case 129:
var10000 = 189;
break;
case 130:
var10000 = 255;
break;
case 131:
var10000 = 78;
break;
case 132:
var10000 = 239;
break;
case 133:
var10000 = 52;
break;
case 134:
var10000 = 252;
break;
case 135:
var10000 = 116;
break;
case 136:
var10000 = 60;
break;
case 137:
var10000 = 193;
break;
case 138:
var10000 = 207;
break;
case 139:
var10000 = 101;
break;
case 140:
var10000 = 142;
break;
case 141:
var10000 = 51;
break;
case 142:
var10000 = 74;
break;
case 143:
var10000 = 76;
break;
case 144:
var10000 = 154;
break;
case 145:
var10000 = 145;
break;
case 146:
var10000 = 105;
break;
case 147:
var10000 = 30;
break;
case 148:
var10000 = 31;
break;
case 149:
var10000 = 27;
break;
case 150:
var10000 = 204;
break;
case 151:
var10000 = 54;
break;
case 152:
var10000 = 7;
break;
case 153:
var10000 = 110;
break;
case 154:
var10000 = 166;
break;
case 155:
var10000 = 123;
break;
case 156:
var10000 = 150;
break;
case 157:
var10000 = 208;
break;
case 158:
var10000 = 115;
break;
case 159:
var10000 = 75;
break;
case 160:
var10000 = 134;
break;
case 161:
var10000 = 36;
break;
case 162:
var10000 = 199;
break;
case 163:
var10000 = 125;
break;
case 164:
var10000 = 210;
break;
case 165:
var10000 = 109;
break;
case 166:
var10000 = 17;
break;
case 167:
var10000 = 71;
break;
case 168:
var10000 = 152;
break;
case 169:
var10000 = 104;
break;
case 170:
var10000 = 178;
break;
case 171:
var10000 = 44;
break;
case 172:
var10000 = 165;
break;
case 173:
var10000 = 87;
break;
case 174:
var10000 = 235;
break;
case 175:
var10000 = 1;
break;
case 176:
var10000 = 220;
break;
case 177:
var10000 = 108;
break;
case 178:
var10000 = 106;
break;
case 179:
var10000 = 148;
break;
case 180:
var10000 = 56;
break;
case 181:
var10000 = 15;
break;
case 182:
var10000 = 250;
break;
case 183:
var10000 = 62;
break;
case 184:
var10000 = 151;
break;
case 185:
var10000 = 26;
break;
case 186:
var10000 = 243;
break;
case 187:
var10000 = 57;
break;
case 188:
var10000 = 172;
break;
case 189:
var10000 = 66;
break;
case 190:
var10000 = 197;
break;
case 191:
var10000 = 223;
break;
case 192:
var10000 = 228;
break;
case 193:
var10000 = 63;
break;
case 194:
var10000 = 19;
break;
case 195:
var10000 = 70;
break;
case 196:
var10000 = 126;
break;
case 197:
var10000 = 164;
break;
case 198:
var10000 = 212;
break;
case 199:
var10000 = 158;
break;
case 200:
var10000 = 227;
break;
case 201:
var10000 = 139;
break;
case 202:
var10000 = 111;
break;
case 203:
var10000 = 91;
break;
case 204:
var10000 = 23;
break;
case 205:
var10000 = 253;
break;
case 206:
var10000 = 147;
break;
case 207:
var10000 = 170;
break;
case 208:
var10000 = 226;
break;
case 209:
var10000 = 97;
break;
case 210:
var10000 = 39;
break;
case 211:
var10000 = 155;
break;
case 212:
var10000 = 79;
break;
case 213:
var10000 = 247;
break;
case 214:
var10000 = 215;
break;
case 215:
var10000 = 233;
break;
case 216:
var10000 = 218;
break;
case 217:
var10000 = 118;
break;
case 218:
var10000 = 175;
break;
case 219:
var10000 = 32;
break;
case 220:
var10000 = 135;
break;
case 221:
var10000 = 18;
break;
case 222:
var10000 = 69;
break;
case 223:
var10000 = 168;
break;
case 224:
var10000 = 242;
break;
case 225:
var10000 = 86;
break;
case 226:
var10000 = 245;
break;
case 227:
var10000 = 45;
break;
case 228:
var10000 = 25;
break;
case 229:
var10000 = 236;
break;
case 230:
var10000 = 180;
break;
case 231:
var10000 = 77;
break;
case 232:
var10000 = 157;
break;
case 233:
var10000 = 73;
break;
case 234:
var10000 = 187;
break;
case 235:
var10000 = 214;
break;
case 236:
var10000 = 232;
break;
case 237:
var10000 = 64;
break;
case 238:
var10000 = 131;
break;
case 239:
var10000 = 9;
break;
case 240:
var10000 = 146;
break;
case 241:
var10000 = 58;
break;
case 242:
var10000 = 47;
break;
case 243:
var10000 = 191;
break;
case 244:
var10000 = 140;
break;
case 245:
var10000 = 185;
break;
case 246:
var10000 = 177;
break;
case 247:
var10000 = 8;
break;
case 248:
var10000 = 90;
break;
case 249:
var10000 = 96;
break;
case 250:
var10000 = 190;
break;
case 251:
var10000 = 184;
break;
case 252:
var10000 = 113;
break;
case 253:
var10000 = 162;
break;
case 254:
var10000 = 217;
break;
default:
var10000 = 84;
}
short var4 = var10000;
int var5 = (var1 & 255) - var4;
if (var5 < 0) {
var5 += 256;
}
int var6 = ((var1 & '\uffff') >>> 8) - var4;
if (var6 < 0) {
var6 += 256;
}
for(int var7 = 0; var7 < var3.length; ++var7) {
int var8 = var7 % 2;
char var10002 = var3[var7];
if (var8 == 0) {
var3[var7] = (char)(var10002 ^ var5);
var5 = ((var5 >>> 3 | var5 << 5) ^ var3[var7]) & 255;
} else {
var3[var7] = (char)(var10002 ^ var6);
var6 = ((var6 >>> 3 | var6 << 5) ^ var3[var7]) & 255;
}
}
d[var2] = (new String(var3)).intern();
}
return d[var2];
}
}
反编译之后有1000行左右
static静态代码块采用异或的方式进行解密,并将结果存到静态变量数组c中
a方法采用流密码方式进行解密,第一个是数组偏移量,第二个是解密密钥。
简单分析之后,可以写出如下代码
package com.vvvtimes.main;
public class ZKMDeCodeString {
private static String dexor(String str) { // 静态代码块改写,使用此方法可得到静态变量的字符串,但是有的是明文有的是密文
char key[] = new char[] { 116, 91, 57, 40, 121, 92, 118 };
char arr[] = str.toCharArray();
for (int i = 0; i < arr.length; i++) {
arr[i] ^= key[i % 7];
}
return new String(arr);
}
private static String decryption(int iv, String str) {// a方法改写,去掉第一个变量,增加需要解密的字符串参数
char[] ch = str.toCharArray();
short key[] = new short[] { 181, 149, 85, 171, 128, 103, 238, 159, 225, 234, 14, 200, 138, 205, 127, 50, 186,
169, 68, 61, 10, 46, 121, 230, 80, 89, 0, 24, 167, 5, 132, 53, 81, 231, 141, 251, 241, 219, 173, 20, 38,
182, 229, 67, 183, 188, 222, 107, 248, 244, 156, 88, 246, 240, 13, 211, 49, 144, 40, 21, 130, 179, 202,
194, 201, 174, 117, 99, 137, 6, 12, 153, 213, 206, 93, 249, 33, 28, 120, 95, 37, 4, 55, 237, 102, 196,
34, 216, 143, 98, 133, 94, 203, 254, 92, 2, 16, 124, 48, 11, 3, 163, 221, 195, 192, 59, 119, 161, 72,
29, 160, 224, 198, 41, 42, 65, 114, 136, 176, 22, 122, 209, 129, 100, 112, 82, 43, 35, 83, 189, 255, 78,
239, 52, 252, 116, 60, 193, 207, 101, 142, 51, 74, 76, 154, 145, 105, 30, 31, 27, 204, 54, 7, 110, 166,
123, 150, 208, 115, 75, 134, 36, 199, 125, 210, 109, 17, 71, 152, 104, 178, 44, 165, 87, 235, 1, 220,
108, 106, 148, 56, 15, 250, 62, 151, 26, 243, 57, 172, 66, 197, 223, 228, 63, 19, 70, 126, 164, 212,
158, 227, 139, 111, 91, 23, 253, 147, 170, 226, 97, 39, 155, 79, 247, 215, 233, 218, 118, 175, 32, 135,
18, 69, 168, 242, 86, 245, 45, 25, 236, 180, 77, 157, 73, 187, 214, 232, 64, 131, 9, 146, 58, 47, 191,
140, 185, 177, 8, 90, 96, 190, 184, 113, 162, 217, 84 };
short v0 = key[ch[0] & 255]; // v0取字符串低8位,取映射
int v1 = (iv & 255) - v0; // iv取低8位 -v0
if (v1 < 0) {
v1 += 256; // 这里相当于&0xff,因为上面做减法位数拓展,通过这个转回
}
int v2 = ((iv & '\uffff') >>> 8) - v0; // iv取高8位 -v0
if (v2 < 0) {
v2 += 256;
}
for (int i = 0; i < ch.length; ++i) {
if (i % 2 == 0) {
ch[i] = (char) (ch[i] ^ v1);
v1 = ((v1 >>> 3 | v1 << 5) ^ ch[i]) & 255;
} else {
ch[i] = (char) (ch[i] ^ v2);
v2 = ((v2 >>> 3 | v2 << 5) ^ ch[i]) & 255;
}
}
return new String(ch);
}
public static void main(String args[]) {
String cipherText = "\u0093íè[Í\u0005ükçÀú";
String[] cipherArray = new String[] { cipherText.substring(0, 5), cipherText.substring(6, 11) };
System.out.println(cipherArray[0]); // íè[Í --> ç¶Ñs´ --> Hello
System.out.println(dexor(cipherArray[0]));
System.out.println(decryption(8444, dexor(cipherArray[0])));
System.out.println(cipherArray[1]); // ükçÀú --> 0Þè --> World
System.out.println(dexor(cipherArray[1]));
System.out.println(decryption(-25829, dexor(cipherArray[1])));
}
}
输出结果如下
后记20180430:找到一个去ZKM字符串混淆的工具,还没试过,有兴趣的读者可以尝试下:https://github.com/GraxCode/ZKM-8-String-Deobfuscator
转载于:https://blog.51cto.com/7317859/2108290