reverse-android-淘最热点&so

发布于:2024-06-21 ⋅ 阅读:(151) ⋅ 点赞:(0)

资源

1.  com.maihan.tredian  2021版 淘最热点  

2. 该 app 没有加壳 ,也没混淆。

登录抓包

POST: https://api.taozuiredian.com/api/v1/auth/login/sms

POST /api/v1/auth/login/sms HTTP/1.1
Content-Type: application/json
Connection: close
Charset: UTF-8
User-Agent: Dalvik/2.1.0 (Linux; U; Android 14; Pixel 6 Build/AP1A.240505.004)
Host: api.taozuiredian.com
Accept-Encoding: gzip
Content-Length: 566

{"sign":"3c0bc27ca0e9a647f32f5e9751d0db63e38849b5","nonce":"8bfisg1718611936552","tzrd":"BwzXzSGFyiPstMIVuzTZb7LzTZzbXRJOFzpbQiIaT7ujUDo3\/3Itq4wx7VQB94J9yQcrD22YICXHDicUiOY8ggIARFsAfdxkYDBJCJN5ScgdFKnF1+ISjECffNemekpceZEtoWiE8Dw8qF5DYd\/RAGF7iNzRF3WoESa4CR2\/JzHhlwW4d8a2HNEPaNGcdwvomjmkQRh17mnDNufFD3YbHeoTId4Gz0h+IzLUuHCLgQFWoUK\/FPYa7epLPvJ0fi5U1wrV+FU+avqDNzGQVyeewhofZU5c511E0ITgSI27IrqBdCwvtpyW29F8T5dsHhmTrkKyJqs43AS\/fAapl7jYuzLz1+P7PPNEATv5y8GVTQJb+xYVZSVeyNpXmpSgkNIiSQVcRG8xw\/tAAOh6LpuZrx4Xay6OlulssTeYvnaAR1k=","timestamp":"1718611936","app_ver":"100"}

HTTP/1.1 400 Bad Request
Server: nginx
Date: Mon, 17 Jun 2024 08:12:03 GMT
Content-Type: application/json
Content-Length: 167
Cache-Control: no-cache, private
Access-Control-Allow-Origin: *
Vary: Origin
Connection: close

{"code":1,"error":{"code":1,"ex_code":0,"exid":"9a16e02c3c1769177721bc0ece924941","message":"\u9a8c\u8bc1\u7801\u4e0d\u6b63\u786e","custom_params":[]},"success":false}

需要一下三个参数逆向:

sign: 3c0bc27ca0e9a647f32f5e9751d0db63e38849b5

nonce: 8bfisg1718611936552

tzrd: BwzXzSGFyiPstMIVuzTZb7LzTZzbXRJOFzpbQiIaT7ujUDo3\/3Itq4wx7VQB94J9yQcrD22YICXHDicUiOY8ggIARFsAfdxkYDBJCJN5ScgdFKnF1+ISjECffNemekpceZEtoWiE8Dw8qF5DYd\/RAGF7iNzRF3WoESa4CR2\/JzHhlwW4d8a2HNEPaNGcdwvomjmkQRh17mnDNufFD3YbHeoTId4Gz0h+IzLUuHCLgQFWoUK\/FPYa7epLPvJ0fi5U1wrV+FU+avqDNzGQVyeewhofZU5c511E0ITgSI27IrqBdCwvtpyW29F8T5dsHhmTrkKyJqs43AS\/fAapl7jYuzLz1+P7PPNEATv5y8GVTQJb+xYVZSVeyNpXmpSgkNIiSQVcRG8xw\/tAAOh6LpuZrx4Xay6OlulssTeYvnaAR1k=

java层分析

1. 搜索 login/sms

2. 搜索sign, tzrd 字段

可以定位到在 MhRequestUtil.a中

tzrd: Base64.encodeToString(AesUtil.b(jSONObject.toString().getBytes(), a.getBytes(), b.getBytes()), 2);

我们在hook 下 encodeToString ()看是不是这里。 encodeToString里面是个AES ,

String a = "AES/CBC/PKCS5Padding"; 从代码中可以看出 是 aes 自吐算法

Java.perform(function(){

    var Base64 = Java.use('android.util.Base64');
    var String =Java.use('java.lang.String');
    // Base64.encodeToString(AesUtil.b(jSONObject.toString().getBytes(), a.getBytes(), b.getBytes()), 2);
    Base64.encodeToString.overload("[B","int").implementation =function(input,flag){
        console.log(' hook encodeToString.overloads("[B","int") ...' );
        console.log(getStackTrace());
        
        var data= this.encodeToString(input,flag);
        console.log('res data origin: ', data);
        console.log('res data string: ', String.$new(data));

        return data;
        
    }


    // public static byte[] b(byte[] bArr, byte[] bArr2, byte[] bArr3) throws Exception {
    var  AesUtil = Java.use('com.maihan.tredian.util.AesUtil');
    AesUtil.b.implementation= function(src,key,key2){ // key =PeMBjWOVbrMgElXO 写死 , key2: VTToNCiifIJ9c2co

        console.log(getStackTrace());
        console.log('src: ',src)
        console.log('key: ',key)
        console.log('key2: ',key2)

        var data= this.b(src,key,key2);
        console.log('res data origin: ', data);
        console.log('res data string: ', String.$new(data));
        return data;
        
    }

    


    function getStackTrace(){
        return Java.use('android.util.Log').getStackTraceString(Java.use("java.lang.Throwable").$new());
    }
})

//frida -UF -l hook_sign_java.js

打印出:

src string:  {"imei2":"null","device_name":"google Pixel 6","code":"6465","imei1":"null","phone":"18051116656","device_udid":"47cba2e1a0c4aed30ddf6687e096ab84","device_id":"2fd841981ec9f6dbb162d6bf7f93de5e","channel":"official","system":"1","from":"app","mac":"02:00:00:00:00:00","os_ver_code":"34","android_id":"e3de5277cf5deadf"}


key string:  PeMBjWOVbrMgElXO


key2 string:  VTToNCiifIJ9c2co

可以看出上面的  几个数据都是 定2量的。

tzrd= Base64(AES(src,key,key2))

sign: TreUtil.sign(a(map, false, false))

sign 是so里面:  System.loadLibrary("tre");

    public static native String sign(String str);

先hook jni 函数,是可以直接hoo的。


Java.perform(function(){

    // hook jni 
    var TreUtil = Java.use('com.maihan.tredian.util.TreUtil');
    TreUtil.sign.implementation = function(args){
        log('hook TreUtil.sign...')
        log(getStackTrace());
        log(' TreUtil.sign str: ',args);
        
        var data= this.sign(args);
        log(' TreUtil.sign res data: ',data);
        return  data;
        
    }
})

主动调用

function call_sign(){
    Java.perform(function(){
        var TreUtil = Java.use('com.maihan.tredian.util.TreUtil');
        var res =TreUtil.sign('app_ver=100&nonce=x78b381718634336095&timestamp=1718634336&tzrd=BwzXzSGFyiPstMIVuzTZb7LzTZzbXRJOFzpbQiIaT7ujUDo3/3Itq4wx7VQB94J9sH6Br/+3ggTlKNhMMCwxI6jU4tNNGc3FdWH77BqPbj8Z3sr6GqVExUuEtetRWq25SWVEnXflbfjvJKBzOb2KAhNYCPx1NP700luPmXN8vRfplwvjHR5uPdw4Ek+X2hLDnq6nl7LM5OkLYEUxztD7ZaP9tWs+62Te35F/Gne+5Yv1XIn1MmoBpHKrgpprn6i/Bg+K29wqxYHPdj0B7bQKB+sUtQX6Jm1li8cKogRBfEZMQgLc1nbbqdr9yHvWdrfFGIGgVRRn6IZc0+UbUkVppXa0g0PRsbVZx+XREEB+LSBbWRYvF1HWmlMsutj4rxP/54/32j+zW9BAIkEN6Uh9T1IoJ/EI5xYF9gNmKdap1vQ=') //直接java方式主动调用 jni函数 也是可以的
        log(res)
    })
}

Hook 这个是可以拿到

 var MhRequestUtil = Java.use('com.maihan.tredian.net.MhRequestUtil');
    // public static String a(Map<String, String> map, boolean z, boolean z2) {
    // map: {nonce=v08rer1718617204105, tzrd=BwzXzSGFyiPstMIVuzTZb7LzTZzbXRJOFzpbQiIaT7ujUDo3/3Itq4wx7VQB94J9sH6Br/+3ggTlKNhMMCwxI6jU4tNNGc3FdWH77BqPbj/vF2YZf1INWi7KKGyEfahgGdD3G3I4mHOAx4Z96YwwDCeT0XfVarRvgvO3PYkw7/Yv7twVlbauY+aeUgNVm7dYNWzXn/4iVvu7vPLKw/mFBc2AEK17L8398+/mGp0QM9itquHM/0Jxo1Vd9MOlIulQ6XT/1bX3GMaYDnV6y2uLvqPZsaG2tG2TJoPPUD3b+tLP7Qn8XKQFU0w9hu04Osbkyc/LXdEva5R3c+CQGyYUdBZYV6fBatWAK58X4e4VOxmYuUrVY5U+jg/KG6wYuFZ7JpPpmzfD0t0p4YA84rp5K6H29+2xQueuEFbID0/T0do=, timestamp=1718617204, app_ver=100}
    MhRequestUtil.a.overload('java.util.Map', 'boolean', 'boolean').implementation = function(m,z,z2){
        log(' hook MhRequestUtil.a...' );
        log(m,z,z2);
        var hm = Java.cast(m,Java.use('java.util.HashMap'));
        log(hm.toString());
        var data= this.a(m,z,z2);
        log("res data: "+data)
        return data;

    }

从lib中找出 libtre.so 是32位的, 用 IDA 32位打开。 在 导出表里面.

需要手动消息第一个参数 JNIEnv *, 第二个 jclass 

nonce:  随机数

 private static String a() {
        Random random = new Random();
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < 6; i++) {
            stringBuffer.append("abcdefghijklmnopqrstuvwxyz0123456789".charAt(random.nextInt(36)));
        }
        return stringBuffer.toString() + System.currentTimeMillis();
    }

so层分析

unidbg 调用so

JNIclass: com.maihan.tredian.util.TreUtil

public static native String sign(String str);

frida hook java JNI 函数, 看输入输出就行。

静态方法
参数1:string 

返回值:  string

代码: 

新建包: com.maihan.tredian.util

新建类: TreUtil

package com.maihan.tredian.util;

import com.alibaba.fastjson.util.IOUtils;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.StringObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;

import java.io.File;



public class TreUtil {

    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    private final DvmClass TreUtil;

    private final boolean logging;

    TreUtil(boolean logging) {
        this.logging = logging;

        emulator = AndroidEmulatorBuilder.for32Bit()
                .setProcessName("com.pocket.snh48")
                .addBackendFactory(new Unicorn2Factory(true))
                .build(); // 创建模拟器实例,要模拟32位或者64位,在这里区分
        final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口
        memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析

        vm = emulator.createDalvikVM(); // 创建Android虚拟机
        vm.setVerbose(logging); // 设置是否打印Jni调用细节
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/maihan/tredian/util/libtre.so"), false); // 加载libttEncrypt.so到unicorn虚拟内存,加载成功以后会默认调用init_array等函数
        dm.callJNI_OnLoad(emulator); // 手动执行JNI_OnLoad函数,一个 cpp 中可以有动态注册也可以静态注册
        module = dm.getModule(); // 加载好的libttEncrypt.so对应为一个模块

        TreUtil = vm.resolveClass("com/maihan/tredian/util/TreUtil");//原先在 app的路径
    }

    private void destroy() {
        IOUtils.close(emulator);
        if (logging) {
            System.out.println("destroy");
        }
    }

    public static void main(String[] args) throws Exception {
        TreUtil test = new TreUtil(true);

        String res = test.sign("app_ver=100&nonce=x78b381718634336095&timestamp=1718634336&tzrd=BwzXzSGFyiPstMIVuzTZb7LzTZzbXRJOFzpbQiIaT7ujUDo3/3Itq4wx7VQB94J9sH6Br/+3ggTlKNhMMCwxI6jU4tNNGc3FdWH77BqPbj8Z3sr6GqVExUuEtetRWq25SWVEnXflbfjvJKBzOb2KAhNYCPx1NP700luPmXN8vRfplwvjHR5uPdw4Ek+X2hLDnq6nl7LM5OkLYEUxztD7ZaP9tWs+62Te35F/Gne+5Yv1XIn1MmoBpHKrgpprn6i/Bg+K29wqxYHPdj0B7bQKB+sUtQX6Jm1li8cKogRBfEZMQgLc1nbbqdr9yHvWdrfFGIGgVRRn6IZc0+UbUkVppXa0g0PRsbVZx+XREEB+LSBbWRYvF1HWmlMsutj4rxP/54/32j+zW9BAIkEN6Uh9T1IoJ/EI5xYF9gNmKdap1vQ=");
        System.out.println(res);
        test.destroy();
    }

    private String sign(String src) {
        StringObject res = TreUtil.callStaticJniMethodObject(emulator, "sign(Ljava/lang/String;)Ljava/lang/String;", src); // 执行Jni方法
        return res.getValue();
    }

}

算法还原