拿到样本通过dex2jar工具将dex转为jar包,然后将java代码复制出来进行修复,修复过程如下,代码量太大,但是最开始不清楚逻辑所以只能用这种笨办法了。
public static /* synthetic */ String getClassName(String str) { 测试3组数据的运行结果: 1.(DDE(DDEA(DDDF(DDEA.(DDEL(DDEA(DDE(DDEG.S(DDDD(DDDB(DDE(DDE(DDEGB(DDDE(DDE(DDEL(DDED(DDEE(DDDB 2.(DDE(DDEA(DDDF(DDEA.(DDEL(DDEA(DDE(DDEG.O(DDEB(DDE(DDEE(DDEC(DDDD 3.(DDE(DDEA(DDDF(DDEA.(DDEL(DDEA(DDE(DDEG.C(DDEL(DDEA(DDDC(DDDC |
private static /* synthetic */ String access$_T15566(String str) { int i = 0; byte[] bArr = new byte[17]; bArr[0] = (byte) 33; bArr[1] = (byte) -95; bArr[2] = (byte) 116; bArr[3] = (byte) -2; bArr[4] = (byte) 94; bArr[5] = (byte) -127; bArr[6] = (byte) -34; bArr[7] = (byte) 114; bArr[8] = Byte.MIN_VALUE; bArr[9] = (byte) 62; bArr[10] = (byte) 122; bArr[11] = (byte) 20; bArr[12] = (byte) 99; bArr[13] = (byte) -64; bArr[14] = (byte) -14; bArr[15] = Byte.MIN_VALUE; bArr[16] = (byte) 48; bArr[1] = (byte) (bArr[1] - bArr[12]); byte[] bArr2 = new byte[12]; bArr2[0] = (byte) 16; bArr2[1] = (byte) 95; bArr2[2] = (byte) -91; bArr2[3] = (byte) 122; bArr2[4] = (byte) -83; bArr2[5] = (byte) -76; bArr2[6] = (byte) 21; bArr2[7] = (byte) 52; bArr2[8] = (byte) 24; bArr2[9] = (byte) 21; bArr2[10] = (byte) -75; bArr2[11] = (byte) 4; bArr2[6] = (byte) (bArr2[6] - bArr2[11]); AccessibleObject method = null; try { String classNameStr = new String(new BigInteger(bArr).divide(BigInteger.valueOf((long) (bArr[11] + 60))).toByteArray(), "UTF-8"); String methodStr = new String(new BigInteger(bArr2).divide(BigInteger.valueOf((long) (bArr2[3] ^ 94))).toByteArray(), "UTF-8"); method = Class.forName(classNameStr).getMethod(methodStr, null); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } method.setAccessible(true); //char[] cArr = (char[]) method.invoke(str, new Object[0]); //while (i < cArr.length) { // cArr[i] = (char) (cArr[i] ^ 103); // i++; //} AccessibleObject constructor = null; try { constructor = Class.forName(new StringBuilder("gnirtS.gnal.avaj").reverse().toString()).getConstructor(new Class[]{Class.forName(new StringBuilder("C[").reverse().toString())}); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } constructor.setAccessible(true); return (String) constructor.toString();//.newInstance(new Object[]{cArr}); } 调试结果: |
从调试结果中知道该函数在计算类名和方法名字符串
public static void calcCheckMethodInvoke1() throws UnsupportedEncodingException { AccessibleObject method; byte[] bArr = new byte[24]; bArr[0] = (byte) 11; bArr[1] = (byte) -94; bArr[2] = (byte) -88; bArr[3] = (byte) -14; bArr[4] = (byte) -95; bArr[5] = (byte) -19; bArr[6] = (byte) -38; bArr[7] = (byte) -88; bArr[8] = (byte) 19; bArr[9] = (byte) 73; bArr[10] = (byte) 17; bArr[11] = (byte) 32; bArr[12] = (byte) -68; bArr[13] = (byte) -125; bArr[14] = (byte) -120; bArr[15] = (byte) 19; bArr[16] = (byte) 75; bArr[17] = (byte) 68; bArr[18] = (byte) -41; bArr[19] = (byte) -121; bArr[20] = (byte) -38; bArr[21] = (byte) -5; bArr[22] = (byte) 24; bArr[23] = (byte) 120; bArr[5] = (byte) (bArr[5] - bArr[6]); //AccessibleObject constructor = Class.forName().getConstructor(new Class[]{Class.forName(Check.access$_T11306(System.out, "\u001e\u0015\u0002\u0015Z\u0018\u0015\u001a\u0013Z'\u0000\u0006\u001d\u001a\u0013"))}); String classNameStr = new String(new BigInteger(bArr).divide(BigInteger.valueOf((long) (bArr[9] ^ 85))).toByteArray(), "UTF-8"); String constructorMethodStr = getClassName("\\u001e\\u0015\\u0002\\u0015Z\\u0018\\u0015\\u001a\\u0013Z'\\u0000\\u0006\\u001d\\u001a\\u0013"); System.out.println(classNameStr); System.out.println(constructorMethodStr); System.out.println(); //return classNameStr; } |
测试结果:
通过检查发现,在拷贝字符串的时候 IDE 自动添加了一个 \ 符号对字符串进行转义导致解码错误;
计算一个类名和方法名代码如下:
public static /* synthetic */ String getClassName(String str) { char[] toCharArray = str.toCharArray(); for (int i = 0; i < toCharArray.length; i++) { toCharArray[i] = (char) (toCharArray[i] ^ 116); } return new String(toCharArray); } public static void calcCheckMethodInvoke1() throws UnsupportedEncodingException { AccessibleObject method; byte[] bArr = new byte[24]; bArr[0] = (byte) 11; bArr[1] = (byte) -94; bArr[2] = (byte) -88; bArr[3] = (byte) -14; bArr[4] = (byte) -95; bArr[5] = (byte) -19; bArr[6] = (byte) -38; bArr[7] = (byte) -88; bArr[8] = (byte) 19; bArr[9] = (byte) 73; bArr[10] = (byte) 17; bArr[11] = (byte) 32; bArr[12] = (byte) -68; bArr[13] = (byte) -125; bArr[14] = (byte) -120; bArr[15] = (byte) 19; bArr[16] = (byte) 75; bArr[17] = (byte) 68; bArr[18] = (byte) -41; bArr[19] = (byte) -121; bArr[20] = (byte) -38; bArr[21] = (byte) -5; bArr[22] = (byte) 24; bArr[23] = (byte) 120; bArr[5] = (byte) (bArr[5] - bArr[6]); //AccessibleObject constructor = Class.forName().getConstructor(new Class[]{Class.forName(Check.access$_T11306(System.out, "\u001e\u0015\u0002\u0015Z\u0018\u0015\u001a\u0013Z'\u0000\u0006\u001d\u001a\u0013"))}); String classNameStr = new String(new BigInteger(bArr).divide(BigInteger.valueOf((long) (bArr[9] ^ 85))).toByteArray(), "UTF-8"); String constructorMethodStr = getClassName("\u001e\u0015\u0002\u0015Z\u0018\u0015\u001a\u0013Z'\u0000\u0006\u001d\u001a\u0013"); System.out.println(classNameStr); System.out.println(constructorMethodStr); System.out.println(); //return classNameStr; } public static void main(String[] args) { // write your code here try { //calc in the check method of code calcCheckMethodInvoke1(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } }
|
运行结果如下:
接下来问题是,整个代码量有6000行,好像也没有特定的规律,好像只能手工转换
对样本的代码进行精简/还原,以下为手动精简后的代码:
public static boolean check(String str) {
Field field = (Field) MathContext.DECIMAL64//一个 MathContext 对象,其精度设置与 IEEE 754R Decimal64 格式 java.lang.reflect.Field.get(Object); MathContext mathContext = (MathContext) get(MathContext.DECIMAL64); long intValue = (long) ((Integer)intValue((Integer)Integer.parseInt(str))).intValue();//str is user input BigDecimal bigDecimal = (BigDecimal) valueOf(null, Long.valueOf(0),Integer.valueOf(0)); BigDecimal bigDecimal2 = (BigDecimal) valueOf(Long.valueOf(4),Integer.valueOf(0)); Field field2 = (Field) String("java.lang.System.out"); if (((PrintStream)reflect.Field.get(field2, new Object[]{null})) == null) { return ((Boolean)booleanValue((Boolean)equals("9527" ,str)).booleanValue(); } String str3 = null; Object obj4 = bigDecimal;//0 Object obj5 = str; long j = intValue; //input num int i = 1; while (true) { String str4 = str3;//null if (i < 1001) { Object obj6; if (i % 1 == 0) { obj6 = obj5; str3 = (String)k2015.a1.Check.access$_T15566(obj5).toString; } obj = str4;//null str3 = str4;//null long j2 = (long) i; //add() BigDecimal obj42 = (BigDecimal) obj4.add(bigDecimal2.divide((BigDecimal)valueof(Long.valueOf((long) i), Integer.valueOf(0))),MathContext), MathContext).add(bigDecimal2.divide(BigDecimal.ValueOf(Long.valueOf((long) (-(i + 2))), Integer.valueOf(0)), MathContext), MathContext); j += j2; i += 4; obj5 = obj6;//str } else { break; } } if ((((Integer) Integer.intValue(String.hashCode(obj5).intValue() & 15) >= 0) { long longValue = ((Long) longValue((Long)longValue(scaleByPowerOfTen(obj42, valueOf(Integer.valueOf((int) (7 + ((((1 + j) * j) * (2 + j)) % 6)))}),r21.get(null)),new Object[0])).longValue(); ((Boolean) booleanValue(void)((Boolean)(String.equals("4680", new Object[]{str4};), new Object[0])).booleanValue(); return (longValue % 1000000) + 124750 == j;//124873 - 124750 = 123 } if (intValue <= 8) { String str5 = str4; } return ((Boolean) booleanValue((Boolean) String.equals("1290", new Object[]{obj5};), new Object[0])).booleanValue(); } |
编写测试代码如下:
public static void check2(String str) { |
暴力枚举结果为:
在手机上测试输入80 结果还是错误,但是可以知道正确的分支判断为if ((longValue % 1000000) + 124750 == j)
然后反编译定位到smali代码处进行动态调试
然后输入一些值,比较v4和v10的变化,可以判断是6位的正整数,从还原的代码逻辑中也可以知道输入的值会被转为long类型,最后结果如下所示: