Java逆向基础之ZKM字符串混淆与还原
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java逆向基础之ZKM字符串混淆与还原相关的知识,希望对你有一定的参考价值。
为了防止静态分析,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?àú --> ?0Tè --> World System.out.println(dexor(cipherArray[1])); System.out.println(decryption(-25829, dexor(cipherArray[1]))); } }
输出结果如下
以上是关于Java逆向基础之ZKM字符串混淆与还原的主要内容,如果未能解决你的问题,请参考以下文章