攻防世界-Mobile-easyjni-最详细分析

攻防世界-Mobile-easyjni

本文首发于博主公众号LISTONE,欢迎关注哦!
博主公众号

这个题分析起来逻辑比上个easyjava要简单许多,做题速度快了很多。

首先还是用jeb打开,打开后首先看这个配置文件(对于这道比较简单的题来说,不看也可,不过为了培养一下好的习惯,看一下比较好)。

在这里插入图片描述

打开配置文件后,就找下面这个标签中的 name 属性。
在这里插入图片描述

然后我们点击那个 dex ,可以看到反编译之后的目录结构了,根据上面那个 name 属性,我们就可以找到主文件。

在这里插入图片描述

接下来还是分析 onCreate 方法。
在这里插入图片描述

看到它调用了 MainActivity.a 方法,我们继续跟进去看看这个 a 方法干了什么。

在这里插入图片描述

可以看到它有调用了另外一个 a 方法,去分析它。

在第二个 a 方法中可以看到有一个关键语句

v0_1 = this.ncheck(new a().a(arg3.getBytes()));

先实例化了一个 a 类的对象,然后调用了 ncheck ,这个ncheck 是我们的 so 文件里的一个函数。

我们先来看这个 a 类的java代码,先对它分析一下,我们再去看 so 文件。
在这里插入图片描述

我们主要分析 a 函数,看到有一个 a.a 静态变量,这时我们猜测,这个是否是变种的base64编码,我们写一个java文件来调用一下这个类里的 a 函数,进行测试一下

public class test{
    public static void main(String[] args){
        String s = "listone";//对listone进行变种base64编码
        System.out.println(new a().a(s.getBytes()));
    }
}

class a {
    private static final char[] a = {'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 a() {
        super();
    }

    public String a(byte[] arg10) {
        int v8 = 3;
        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) {
                if(v0 + v3 <= arg10.length - 1) {
                    v5[v3] = ((byte)(v2 | (arg10[v0 + v3] & 255) >>> v3 * 2 + 2));
                    v2 = ((byte)(((arg10[v0 + v3] & 255) << (2 - v3) * 2 + 2 & 255) >>> 2));
                }
                else {
                    v5[v3] = v2;
                    v2 = 64;
                }

                ++v3;
            }

            v5[v8] = v2;
            int v2_1;
            for(v2_1 = 0; v2_1 <= v8; ++v2_1) {
                if(v5[v2_1] <= 63) {
                    v4.append(a[v5[v2_1]]);
                }
                else {
                    v4.append('=');
                }
            }
        }

        return v4.toString();
    }
}

将上面的代码保存为 test.java ,然后在终端运行
在这里插入图片描述

可以看到对 listone 的加密结果为 bSt9kSRzQ3==

然后我又找了一个python实现的对变种base64的加密实现。

base64_charset = ['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']

def encode(origin_bytes):
    # 将每一位bytes转换为二进制字符串
    base64_bytes = ['{:0>8}'.format(str(bin(b)).replace('0b', '')) for b in origin_bytes]

    resp = ''
    nums = len(base64_bytes) // 3
    remain = len(base64_bytes) % 3

    integral_part = base64_bytes[0:3 * nums]
    while integral_part:
        # 取三个字节,以每6比特,转换为4个整数
        tmp_unit = ''.join(integral_part[0:3])
        tmp_unit = [int(tmp_unit[x: x + 6], 2) for x in [0, 6, 12, 18]]
        # 取对应base64字符
        resp += ''.join([base64_charset[i] for i in tmp_unit])
        integral_part = integral_part[3:]

    if remain:
        # 补齐三个字节,每个字节补充 0000 0000
        remain_part = ''.join(base64_bytes[3 * nums:]) + (3 - remain) * '0' * 8
        # 取三个字节,以每6比特,转换为4个整数
        # 剩余1字节可构造2个base64字符,补充==;剩余2字节可构造3个base64字符,补充=
        tmp_unit = [int(remain_part[x: x + 6], 2) for x in [0, 6, 12, 18]][:remain + 1]
        resp += ''.join([base64_charset[i] for i in tmp_unit]) + (3 - remain) * '='

    return resp

if __name__ == '__main__':
    s = 'listone'.encode()
    local_base64 = encode(s)
    print('使用本地base64加密:', local_base64)

然后我们对 listone 进行加密

在这里插入图片描述

可以看到结果与java运行的一致,也因此证明了我们的猜想。

通过解压软件解压apk文件,然后可以在lib目录里找到这个so文件。

接下来就是看 so 文件里的 ncheck 函数是干什么的了。

在这里插入图片描述

这个v6我们可以大胆地猜测它为我们传入的字符串

在这里插入图片描述

然后我们看那个循环干了什么

v7 = 0;
do
{
    //将s1[v7]的地址赋值给v8
    v8 = &s1[v7];
    //第一轮就是 s1[0] = v6[16]
    s1[v7] = v6[v7 + 16];
    //第一轮就是先 v9 = v6[0],然后v7 = v7+1 = 1
    v9 = v6[v7++];
    //第一轮是v8[16]=s1[16]=v9
    v8[16] = v9;
}
while ( v7 != 16 );

分析之后就可以知道,这个循环就是将我们传入的字符串的前16位与后16位字符对调。

然后继续分析下一个循环

v10 = 0;
do
{
    v12 = __OFSUB__(v10, 30);
    v11 = v10 - 30 < 0;
    
    //第一轮v16=s1[0]
    v16 = s1[v10];
    //第一轮s1[0] = s1[1]
    s1[v10] = s1[v10 + 1];
    //第一轮s1[1] = v16 = s1[0]
    s1[v10 + 1] = v16;
    v10 += 2;
}
while ( v11 ^ v12 );

可以发现这个循环是将字符串两两对调位置。

现在再来分析循环的结束条件,这个结束条件一开始直接把我整懵逼了,查了些资料,__OFSUB__ 表示x-y是否溢出,溢出返回1,没有溢出返回0。在此情况下,应该不存在溢出,则返回均为0,即v12=0,当v10>30时,v11=0。此时异或得0,循环结束。

这里返回值是与一个字符串比较的结果,那我们就将该字符串逆推回去,写出逆向脚本。

在这里插入图片描述

写出脚本为:

def decode(base64_str):
    base64_charset = "i5jLW7S0GX6uf1cv3ny4q8es2Q+bdkYgKOIT/tAxUrFlVPzhmow9BHCMDpEaJRZN"
    base64_bytes = ['{:0>6}'.format(str(bin(base64_charset.index(s))).replace('0b', '')) for s in base64_str if
                    s != '=']
    resp = bytearray()
    nums = len(base64_bytes) // 4
    remain = len(base64_bytes) % 4
    integral_part = base64_bytes[0:4 * nums]

    while integral_part:
        tmp_unit = ''.join(integral_part[0:4])
        tmp_unit = [int(tmp_unit[x: x + 8], 2) for x in [0, 8, 16]]
        for i in tmp_unit:
            resp.append(i)
        integral_part = integral_part[4:]

    if remain:
        remain_part = ''.join(base64_bytes[nums * 4:])
        tmp_unit = [int(remain_part[i * 8:(i + 1) * 8], 2) for i in range(remain - 1)]
        for i in tmp_unit:
            resp.append(i)

    return resp

if __name__ == '__main__':
    flag_1 = 'MbT3sQgX039i3g==AQOoMQFPskB1Bsc7'
    print('加密后的字符串:',flag_1)
    # 前后16位调换位置
    flag_2 = flag_1[len(flag_1)//2:] + flag_1[0:len(flag_1)//2]
    print('前后16位调换位置:',flag_2)
    flag_3 = ''
    for i in range(len(flag_2)//2):
        flag_3 += flag_2[i*2+1] + flag_2[i*2]
    print('前后两位调换位置:',flag_3)
    print('解密后:',decode(flag_3))

最后运行脚本:

在这里插入图片描述

就解出flag的值了。

这个题总体来说逻辑比上一个easyjava要简单一些,不过这个涉及到了对so文件的简单分析,也算是学到了一个小知识吧。

原创文章 10 获赞 3 访问量 453

猜你喜欢

转载自blog.csdn.net/cookieXSS/article/details/106175001