Android逆向题解4-EasyJni

-运行界面:
找flag,用户输入一串字符串点击cheek确认输入是否正确
在这里插入图片描述

  • 代码分析
    判断用户输入字符串是否正确
    在这里插入图片描述
    先用一个a方法加密之后传入native函数ncheck进行判定;也就是在Java层有一次加密,在native层还有一次加密;
    在这里插入图片描述
    Java层的加密,类似一个换了码表的base64加密;
package com.a.easyjni;
 
public class a {
    private static final char[] a;
 
    static {
        a.a = new char[]{'i', '5', 'j', 'L', 'W', '7', 'S', '0', 'G', 'X', '6', 'u', 'f', '1', 'c', 'v', '3', 'n', 'y', '4', 'q', '8', 'e', 's', '2', 'Q', '+', 'b', 'd', 'k', 'Y', 'g', 'K', 'O', 'I', 'T', '/', 't', 'A', 'x', 'U', 'r', 'F', 'l', 'V', 'P', 'z', 'h', 'm', 'o', 'w', '9', 'B', 'H', 'C', 'M', 'D', 'p', 'E', 'a', 'J', 'R', 'Z', 'N'};
    }
 
    public String a(byte[] arg10) {
        StringBuilder v4 = new StringBuilder();
        int v0;
        for(v0 = 0; v0 <= arg10.length - 1; v0 += 3) {
            byte[] v5 = new byte[4];
            int v3 = 0;
            byte v2 = 0;
            while(v3 <= 2) {
                int v7 = arg10.length - 1;
                if(v0 + v3 <= v7) {
                    v5[v3] = (byte)(v2 | (arg10[v0 + v3] & 0xFF) >>> v7 * 2 + 2);
                    v2 = (byte)(((arg10[v0 + v3] & 0xFF) << (2 - v3) * 2 + 2 & 0xFF) >>> 2);
                }
                else {
                    v5[v3] = v2;
                    v2 = 0x40;
                }
 
                ++v3;
            }
 
            v5[3] = v2;
            int v2_1;
            for(v2_1 = 0; v2_1 <= 3; ++v2_1) {
                if(v5[v2_1] <= 0x3F) {
                    v4.append(a.a[v5[v2_1]]);
                }
                else {
                    v4.append('=');
                }
            }
        }
 
        return v4.toString();
    }
}

so层的加密函数Java_com_a_easyjni_MainActivity_ncheck,最后比较的字符串是"MbT3sQgX039i3gAQOoMQFPskB1Bsc7",
这个也就是我们输入的字符串通过2次加密之后的结果;
这里面的加密逻辑比较简单,就是前16位和后16位互换,然后第i位和i+1位互换,i位偶数和0,也就是0、1互换,2、3互换,4、5互换。。。;
前后16位互换之后 : AQOoMQFPskB1Bsc7MbT3sQgX039i3g

然后i和i+1位互换后:QAoOQMPFks1BsB7cbM3TQsXg30i9g3==

signed int __fastcall Java_com_a_easyjni_MainActivity_ncheck(JNIEnv *a1, int a2, int key)
{
  int v3; // r8
  JNIEnv *v4; // r5
  int v5; // r8
  const char *v6; // r6
  int v7; // r0
  char *v8; // r2
  char v9; // r1
  int v10; // r0
  bool v11; // nf
  unsigned __int8 v12; // vf
  int v13; // r1
  signed int result; // r0
  char s1[32]; // [sp+3h] [bp-35h]
  char v16; // [sp+23h] [bp-15h]
  int v17; // [sp+28h] [bp-10h]
 
  v17 = v3;
  v4 = a1;
  v5 = key;
  v6 = ((*a1)->GetStringUTFChars)(a1, key, 0);
  if ( strlen(v6) == 32 )
  {
    v7 = 0;
    do
    {
      v8 = &s1[v7];
      s1[v7] = v6[v7 + 16];
      v9 = v6[v7++];
      v8[16] = v9;                              // s1前16位=key后16位
    }                                           // s1后16位=key前16位
    while ( v7 != 16 );
    ((*v4)->ReleaseStringUTFChars)(v4, v5, v6);
    v10 = 0;
    do
    {
      v12 = __OFSUB__(v10, 30);                 // v10-30是否溢出
      v11 = v10 - 30 < 0;
      v16 = s1[v10];
      s1[v10] = s1[v10 + 1];
      s1[v10 + 1] = v16;
      v10 += 2;                                 // s[i]和s[i+1],i为0和偶数,互换位置,就是0和1、2和3、4和5互换;
    }
    while ( v11 ^ v12 );
    v13 = memcmp(s1, "MbT3sQgX039i3g==AQOoMQFPskB1Bsc7", ' '); //这里比较判断,也就是加密字符串是"MbT3sQgX039i3g==AQOoMQFPskB1Bsc7"
    result = 0;
    if ( !v13 )
      result = 1;
  }
  else
  {
    ((*v4)->ReleaseStringUTFChars)(v4, v5, v6);
    result = 0;
  }
  return result;
}

上面so里面解密出来的结果就是Java层传过去的值,也就是第4步得到的解密值就是第3步的加密函数的返回值:QAoOQMPFks1BsB7cbM3TQsXg30i9g3==
第3步前面也说过了就是一个换了码表的base64加密,拿我们的返回值解密之后就得到flag:flag{just_ANot#er_@p3}
解密代码:

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
 
public class a {
 
    public static void main(String[] args) {
        char[] a = new char[]{'i', '5', 'j', 'L', 'W', '7', 'S', '0', 'G', 'X', '6', 'u', 'f', '1',
                'c', 'v', '3', 'n', 'y', '4', 'q', '8', 'e', 's', '2', 'Q', '+', 'b', 'd', 'k', 'Y',
                'g', 'K', 'O', 'I', 'T', '/', 't', 'A', 'x', 'U', 'r', 'F', 'l', 'V', 'P', 'z', 'h',
                'm', 'o', 'w', '9', 'B', 'H', 'C', 'M', 'D', 'p', 'E', 'a', 'J', 'R', 'Z', 'N'}; //自定义的码表
        String s ="QAoOQMPFks1BsB7cbM3TQsXg30i9g3==";//加密后的base64值
        byte[] bytes=s.getBytes();
        char[] b= getChars(bytes);
        int[] sy = new int[32];
        for(int i=0;i<b.length;i++){
            for (int j=0;j<a.length;j++){
                if(a[j]==b[i]){
                    sy[i]=j;                   //得到索引值
                    //System.out.println(sy[i]);
                }
            }
        }
        //每4位为一组进行移位和组合得到3位解密的flag
        byte[] bflag = new byte[32];
        int n=0;
        for(int m=0;m<sy.length-3;m+=4){
            bflag[n]=(byte)((sy[m]<<2)|(sy[m+1]>>4));     //bflag[0]
            bflag[n+1]=(byte)((sy[m+1]<<4)|(sy[m+2]>>2)); //bflag[1]
            bflag[n+2]=(byte)((sy[m+2]<<6)|(sy[m+3]));    //bflag[2]
            n+=3;
        }
        String flag =String.valueOf(getChars(bflag));
        System.out.println(flag);
 
    }
    public static char[] getChars(byte[] bytes) {
        Charset cs = Charset.forName("UTF-8");
        ByteBuffer bb = ByteBuffer.allocate(bytes.length);
        bb.put(bytes);
        bb.flip();
        CharBuffer cb = cs.decode(bb);
        return cb.array();
    }
 
 
}

又翻到了这个重新解了一遍,补充个代码:

import java.io.ByteArrayOutputStream;

public class test {
    public static void main(String[] args) throws Exception {
        String v12="MbT3sQgX039i3g==AQOoMQFPskB1Bsc7";
        char[] v5=new char[32];//flag

        //字符串转char[]
        char[] v12c=v12.toCharArray();

        //单双位互换
        for(int i=0;i<32;i+=2){
            char v13=v12c[i];
            v12c[i]=v12c[i+1];
            v12c[i+1]=v13;
        }
        System.out.println(String.valueOf(v12c));

        //前16位和后16位互换
        for (int j=0;j!=16;++j){
             v5[j + 16]=v12c[j];
            char v8=v12c[16+j];
            v5[j]=v8;
        }
        System.out.println(String.valueOf(v5));
        generateDecoder();
        System.out.println(new String(Base64Decode(String.valueOf(v5))));
    }

    private static final char[] Base64ByteToStr = new char[]{'i', '5', 'j', 'L', 'W', '7', 'S', '0', 'G', 'X', '6', 'u', 'f', '1', 'c', 'v', '3', 'n', 'y', '4', 'q', '8', 'e', 's', '2', 'Q', '+', 'b', 'd', 'k', 'Y', 'g', 'K', 'O', 'I', 'T', '/', 't', 'A', 'x', 'U', 'r', 'F', 'l', 'V', 'P', 'z', 'h', 'm', 'o', 'w', '9', 'B', 'H', 'C', 'M', 'D', 'p', 'E', 'a', 'J', 'R', 'Z', 'N'};

    //自定义码表base64解码
    private static byte[] StrToBase64Byte = new byte[128];
    private static final int RANGE = 0xff;
    private static byte[] Base64Decode(String val) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();//destination bytes, valid string that we want
        byte[] srcBytes = val.getBytes();
        byte[] base64bytes = new byte[srcBytes.length];
        //get the base64 bytes (the value is -1 or 0 ~ 63)
        for(int i = 0; i <= srcBytes.length - 1; i++) {
            int ind = (int) srcBytes[i];
            base64bytes[i] = StrToBase64Byte[ind];
        }
        //base64 bytes (4 bytes) to normal bytes (3 bytes)
        for(int i = 0; i <= base64bytes.length - 1; i+=4) {
            byte[] deBytes = new byte[3];
            int delen = 0;// if basebytes[i] = -1, then debytes not append this value
            byte tmp ;
            for(int k = 0; k <= 2; k++) {
                if((i + k + 1) <= base64bytes.length - 1 && base64bytes[i + k + 1] >= 0) {
                    tmp = (byte) (((int)base64bytes[i + k + 1] & RANGE) >>> (2 + 2 * (2 - (k + 1))));
                    deBytes[k] = (byte) ((((int) base64bytes[i + k] & RANGE) << (2 + 2 * k) & RANGE) | (int) tmp);
                    delen++;
                }
            }
            for(int k = 0; k <= delen - 1; k++) {
                bos.write((int)deBytes[k]);
            }
        }
        return bos.toByteArray();
    }

    private static void generateDecoder() throws Exception {
        for(int i = 0; i <= StrToBase64Byte.length - 1; i++) {
            StrToBase64Byte[i] = -1;
        }
        for(int i = 0; i <= Base64ByteToStr.length - 1; i++) {
            StrToBase64Byte[Base64ByteToStr[i]] = (byte)i;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Android逆向小菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值