浅笑博客
每当对这个世界感到绝望的时候,买一包泡面,然后告诉自己:我们的泡面是有酱包的。
浅笑博客
对一某手解析工具App的逆向接口分析
对一某手解析工具App的逆向接口分析

最近,原本的那个快手接口口失效了(后期有时间再尝试修复),一时想找个用下,找到了这么一个工具:快手解析工具App。测试发现可以用。为了方便使用,于是想逆向提取其中的接口,于是准备逆向。

首先抓包无果,因为软件好像有防抓包网络检测,于是准备从源码入手。

首先App被360加固,第一步肯定要脱一下dex,这里我使用的是反射大师xposed模块,由于我手机Android9不兼容,于是在vmos虚拟机中尝试。我们只需将软件运行时加载的dex dump出来即可,因此只需准备好反射大师,选择并打开App,然后选择写出dex即可。

http://blog.qianxiao.fun/wp-content/uploads/2020/08/图片-492x1024.png
http://blog.qianxiao.fun/wp-content/uploads/2020/08/图片-1-492x1024.png

然后用mt管理器对写出的dex修复一下,以能打开查看。

由于发现e4a字样,软件应为e4a(中文安卓编程,类似易语言)编写,因此分析起来更是非常方便。直接通过解析时提示的关键字“正在获取资源”进行搜索定位。(搜索字符串关键词是逆向过程中常用的代码定位方法,此外搜不到关键词也可以通过布局id定位等方法)

http://blog.qianxiao.fun/wp-content/uploads/2020/08/图片-2-492x1024.png
http://blog.qianxiao.fun/wp-content/uploads/2020/08/图片-3-492x1024.png

转成java查看,结果也是一目了然。由上图可知,请求接口中url为要解析的链接,time为当前时间戳,然后就是根据url和time生成的一个签名值。所以只需看看公用模块.encode方法时如何进行签名的。由于现在MT管理器使用起来非常方便,直接选择smali代码后选择跳转即可,或代码搜索调用(->)也可以。跳转过去查看,也是发现非常的清楚(毕竟是中文源码)。

http://blog.qianxiao.fun/wp-content/uploads/2020/08/图片-4-492x1024.png

签名算法大家应该都能看懂,接下来我直接用java实现测试一下:

//:ksjx/KSJX.java
package ksjx;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;

import utils.Md5Utils;
import utils.Rc4Utils;

public class KSJX {
    public static void main(String[] args) throws IOException {
        String a = "http://appjiekou.dizhan666.cn/ShortVideo.php?url=%1$s&time=%2$s&sign=%3$s";
        String url = "https://v.kuaishou.com/6cczf6";
        System.out.println("url = " + url);
        String ts = String.valueOf(System.currentTimeMillis()/1000);
        String sign = sign(ts,url);
        a = String.format(a, url,ts,sign);
        System.out.println(a);
        System.out.println(get(a));
    }

    static String sign(String p1,String p2){
        p1 = Md5Utils.md5gbk(p1).toLowerCase();
        return p1.substring(0,16)+ Rc4Utils.encry_RC4_string(p2,p1) +p1.substring(p1.length()-16);
    }

    static String get(String url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .build();
        try (Response response = new OkHttpClient().newCall(request).execute()) {
            return response.body().string();
        }
    }
}
//:utils/Rc4Utils.java
package utils;

public class Rc4Utils {

    public static String decry_RC4(byte[] data, String key) {
        if (data == null || key == null) {
            return null;
        }
        return asString(RC4Base(data, key));
    }

    public static String decry_RC4(String data, String key) {
        if (data == null || key == null) {
            return null;
        }
        return new String(RC4Base(HexString2Bytes(data), key));
    }

    public static byte[] encry_RC4_byte(String data, String key) {
        if (data == null || key == null) {
            return null;
        }
        byte b_data[] = data.getBytes();
        return RC4Base(b_data, key);
    }

    public static String encry_RC4_string(String data, String key) {
        if (data == null || key == null) {
            return null;
        }
        return toHexString(asString(encry_RC4_byte(data, key)));
    }

    private static String asString(byte[] buf) {
        StringBuffer strbuf = new StringBuffer(buf.length);
        for (int i = 0; i < buf.length; i++) {
            strbuf.append((char) buf[i]);
        }
        return strbuf.toString();
    }

    private static byte[] initKey(String aKey) {
        byte[] b_key = aKey.getBytes();
        byte state[] = new byte[256];

        for (int i = 0; i < 256; i++) {
            state[i] = (byte) i;
        }
        int index1 = 0;
        int index2 = 0;
        if (b_key == null || b_key.length == 0) {
            return null;
        }
        for (int i = 0; i < 256; i++) {
            index2 = ((b_key[index1] & 0xff) + (state[i] & 0xff) + index2) & 0xff;
            byte tmp = state[i];
            state[i] = state[index2];
            state[index2] = tmp;
            index1 = (index1 + 1) % b_key.length;
        }
        return state;
    }

    private static String toHexString(String s) {
        String str = "";
        for (int i = 0; i < s.length(); i++) {
            int ch = (int) s.charAt(i);
            String s4 = Integer.toHexString(ch & 0xFF);
            if (s4.length() == 1) {
                s4 = '0' + s4;
            }
            str = str + s4;
        }
        return str;// 0x表示十六进制
    }

    private static byte[] HexString2Bytes(String src) {
        int size = src.length();
        byte[] ret = new byte[size / 2];
        byte[] tmp = src.getBytes();
        for (int i = 0; i < size / 2; i++) {
            ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
        }
        return ret;
    }

    private static byte uniteBytes(byte src0, byte src1) {
        char _b0 = (char) Byte.decode("0x" + new String(new byte[] { src0 })).byteValue();
        _b0 = (char) (_b0 << 4);
        char _b1 = (char) Byte.decode("0x" + new String(new byte[] { src1 })).byteValue();
        byte ret = (byte) (_b0 ^ _b1);
        return ret;
    }

    private static byte[] RC4Base(byte[] input, String mKkey) {
        int x = 0;
        int y = 0;
        byte key[] = initKey(mKkey);
        int xorIndex;
        byte[] result = new byte[input.length];

        for (int i = 0; i < input.length; i++) {
            x = (x + 1) & 0xff;
            y = ((key[x] & 0xff) + y) & 0xff;
            byte tmp = key[x];
            key[x] = key[y];
            key[y] = tmp;
            xorIndex = ((key[x] & 0xff) + (key[y] & 0xff)) & 0xff;
            result[i] = (byte) (input[i] ^ key[xorIndex]);
        }
        return result;
    }
}
//:utils/Md5Utils.java
package utils;

import java.security.MessageDigest;

public class Md5Utils {

    public static String md5(String arg2) {
        try {
            return a(MessageDigest.getInstance("MD5").digest(arg2.getBytes("UTF-8")));
        }
        catch(Exception v2) {
            v2.printStackTrace();
            return "";
        }
    }

    public static String md5gbk(String arg2) {
        try {
            return a(MessageDigest.getInstance("MD5").digest(arg2.getBytes("GBK")));
        }
        catch(Exception v2) {
            v2.printStackTrace();
            return "";
        }
    }

    private static String a(byte[] arg5) {
        StringBuilder v0 = new StringBuilder();
        int v2;
        for(v2 = 0; v2 < arg5.length; ++v2) {
            int v3 = arg5[v2];
            if(v3 < 0) {
                v3 += 0x100;
            }

            if(v3 < 16) {
                v0.append("0");
            }

            v0.append(Integer.toHexString(v3));
        }

        return v0.toString();
    }
}

测试结果:

http://blog.qianxiao.fun/wp-content/uploads/2020/08/图片-5-1024x615.png

今天的文章比较简单,相信大家都能看懂。我也逆向过更难的,其实再难的,也是这样一步一步来分析逆向,主要是我们要有一个逆向思维,还有要坚持不断尝试。逻辑其实通过反编译dex大多都能看到,但更多时候我们会碰到各种代码混淆来干扰我们分析,这时其实只需慢慢一步一步分析,比如说找到一个关键去找哪里调用它等等,还是能找出结果的。

如文章有错误之处,欢迎不吝指正。技术交流,欢迎评论或与我私信。

2020年9月27日补充:

最近发现他这个接口实现了,返回接口维护。又找了一个目前能用的接口,分享一下:

http://desa.5gzvip.idcfengye.com/jx?url={分享地址(可带文字)}

没有标签
首页      Android逆向      对一某手解析工具App的逆向接口分析

发表评论

textsms
account_circle
email

浅笑博客

对一某手解析工具App的逆向接口分析
最近,原本的那个快手接口口失效了(后期有时间再尝试修复),一时想找个用下,找到了这么一个工具:快手解析工具App。测试发现可以用。为了方便使用,于是想逆向提取其中的接口,于是准…
扫描二维码继续阅读
2020-08-21