APP逆向 day26unidbg下-pdd(anti)案例

news2024/9/25 13:25:46

一.前言

今天我们讲unidbg的下篇,也就是unidbg基础的最后一个部分,我们上节课也有补环境,比如补java环境,补安卓环境,这节课我们讲的肯定比这些都要难,我会给出一个之前讲过的案例,然后会讲一个全新的案例,pdd,这个里面的环境就更加难了,让我们接着往下看吧

二.B站sign参数

2.1 回顾sign

不记得的可以去往期回顾一下,这里给出地址

APP逆向 day18某站逆向 part3-CSDN博客文章浏览阅读1.2k次,点赞23次,收藏19次。因为之前被封,所以很久才更新,今天这个主要是后面的两个hook脚本很重要,app逆向是真的难,真的难找,很大一部分比的就是大家的hook代码。https://blog.csdn.net/weixin_74178589/article/details/140632798?spm=1001.2014.3001.5502我这里给出几张重要的截图

 我们找到这个this.b就是sign值

 

this.b是signedQuery初始化传入的第二个参数

在这里传媒如一个map对象最后tostring 

 

最后执行s在c中进行加密

加密的so文件时libbili.so

2.2 分析

1 so文件和方法
    libbili.so
    static native SignedQuery s(SortedMap<String, String> sortedMap);
2 入参和返回值
    入参:sortedMap---》hook得到数据--》包裹--》传给unidbg
    返回值:SignedQuery类型---》app自己定义的类型
        
        
3 hook 参数和返回值
入参:
map= {actual_played_time=0, aid=884507902, appkey=1d8b6e7d45233436, auto_play=0, build=6240300, c_locale=zh-Hans_CN, channel=xxl_gdt_wm_253, cid=233035308, epid=0, epid_status=, from=2, from_spmid=main.ugc-video-detail.0.0, last_play_progress_time=0, list_play_time=0, max_play_progress_time=0, mid=0, miniplayer_play_time=0, mobi_app=android, network_type=1, paused_time=0, platform=android, play_status=0, play_type=1, played_time=0, quality=32, s_locale=zh-Hans_CN, session=907a218ff7237664b2910757062ce5a40d1f08fc, sid=0, spmid=main.ugc-video-detail.0.0, start_ts=0, statistics={"appId":1,"platform":3,"version":"6.24.0","abtest":""}, sub_type=0, total_time=0, type=3, user_status=0, video_duration=674}
返回值= actual_played_time=0&aid=884507902&appkey=1d8b6e7d45233436&auto_play=0&build=6240300&c_locale=zh-Hans_CN&channel=xxl_gdt_wm_253&cid=233035308&epid=0&epid_status=&from=2&from_spmid=main.ugc-video-detail.0.0&last_play_progress_time=0&list_play_time=0&max_play_progress_time=0&mid=0&miniplayer_play_time=0&mobi_app=android&network_type=1&paused_time=0&platform=android&play_status=0&play_type=1&played_time=0&quality=32&s_locale=zh-Hans_CN&session=907a218ff7237664b2910757062ce5a40d1f08fc&sid=0&spmid=main.ugc-video-detail.0.0&start_ts=0&statistics=%7B%22appId%22%3A1%2C%22platform%22%3A3%2C%22version%22%3A%226.24.0%22%2C%22abtest%22%3A%22%22%7D&sub_type=0&total_time=0&ts=1716387614&type=3&user_status=0&video_duration=674&sign=ed5a33857d99b8442a3fe182aa214eed


4 拼凑出SortedMap--》包裹后---》传给unidbg
    包裹方式一:ProxyDvmObject.createObject(vm,map)
    包裹方式一:vm.resolveClass("类").newObject(map)
    
5 返回值:SignedQuery类型
    app自定义类型---》可以DvmObject<?>类型泛指---》unidbg中所有类型,父类都是DvmObject---》StringObject也是DvmObject---》拿到具体的值DvmObject对象.getValue()
    

6 是否需要不环境?libbili.so中的s方法
    -so内部一定执行了  c调用java
        -c中是没有SignedQuery类的--》返回值是SignedQuery的对象
        -于是:c内部一定调用了java

7 所以在c内部调用java,完成SignQuery的初始化--》必须传入两个参数
而第二个参数,就是sign的值
          public SignedQuery(String str, String str2) {
                this.a = str;
                this.b = str2;
            }

8 分析:so内部--》传入sortedMap,通过某种加密方式加密---》通过c调用java,实例化得到了SignedQuery对象,第一个参数是:sortedMap转成了字符串,第二个参数就是  sign 的加密结果

9 使用unidbg运行时,一定需要补环境--》SignedQuery--》两种方案破解sign
    -1 在需要补SignedQuery环境时,打印出第二个参数,其实就是sign
    -2 完整补SignedQuery,自己把返回的SignedQuery对象,调用toString,返回整个字符串
 

这里我会讲第二种,讲第二种的时候也能讲到第一种,那我们先编写大致逻辑框架,除sign函数以外的直接c就好了

2.3 unidbg运行

 我们直接把unidbg初始化写好,然后运行发现不报错,那么我们现在就要开始编写sign部分的代码了

编写sign中的内容

这里主要是 SignedQuery是apk内部定义的,我们这里没有,所以用一个 DvmObject<?>来接受,这个是所有返回值的父类,然后最后返回 String res=singedQuery.getValue().toString();调用他内部的tostring方法,这样就能跑出最后结果

这里给出编写sign的代码

    public void sign(){
        // 0 构造SortedMap
        SortedMap<String, String> map=new TreeMap<String, String>();
        // 这不全--》我们目标是把sign跑出来,所以暂时传这一点点
        map.put("actual_played_time", "0");
        map.put("aid", "884507902");
        map.put("appkey", "1d8b6e7d45233436");
        map.put("auto_play", "0");
        map.put("ts", "1647952932");

        // 1 找到java类中jni的类  native方法,找的时候是固定写法
        DvmClass LibBili = vm.resolveClass("com/bilibili/nativelibrary/LibBili");

        // 这个返回值是apk内部的,包名类名前面加个L
        String method = "s(Ljava/util/SortedMap;)Lcom/bilibili/nativelibrary/SignedQuery;";

        //3 执行方法
        // 之前我们写的返回值都是StringObject 或者ByteArray
        // 但是这个前面分析他的返回值类型是signedQuery 而这个是apk内部定义的类,我们此时就可以使用DvmObject<?> ,这个是unidbg中所有返回值的父类
        DvmObject<?> singedQuery = LibBili.callStaticJniMethodObject(
                emulator,
                method,
                ProxyDvmObject.createObject(vm, map)
        );

        // 4 打印结果
        // singedQuery.getValue()--->SignedQuery 的对象-->有个toString方法--->返回字符串--》带sign的字符串,这样结果就是字符串类型了
        String res=singedQuery.getValue().toString();
        System.out.println(res);

    }

2.4 补环境

运行肯定会报错,这里我们就需要补环境

 这个就是传入一个字典,判断是否为空

我们有两种操作,一种直接返回True(因为我们知道传入的字典不为空),另一种就是主动调用

那我们又来补这个,这个不就是字典根据键来取值嘛,应该是字符串类型的,只是他用了object来泛指

 这里我们知道是string类型,所以可以用方式二来写,如果不知道的话,可以直接用第一种方便的来

我们运行看看

这个不就是调用了java中自己定义的方法吗

这个我就带着大家一步步来写

我们按照他的逻辑来,是不是补成这样,但是少了 SignedQuery类,还有里面的r方法,那我们去源代码里面扣一个来

把这个r方法扣进来,发现报错的有 TextUtils.isEmpty, b, ContainerUtils.FIELD_DELIMITER

很明显b是里面定义的方法,那TextUtils.isEmpty是什么呢,他是安卓中的判断是否为空,可以去chatgpt问一下,这里是不是可以直接调用string中的内置方法isEmpty()来替换呢ContainerUtils.FIELD_DELIMITER我们双击点进去发现是"&",也可以直接替换

b方法直接c就行了

我们把那些修改,再把b给扣进去,发现又需要c,我们再扣,这后面我就不说了,流程都一样,接着扣,修改就行了,补完之后,我们再运行

那我们就接着补,我们补之前先来分析一下这个代码,这个不就是SignedQuery类实例化的时候传入两个参数嘛,而这个第二个参数不就是我们要的sign值嘛,当时我们说了可以完整补完,也可以部分补,那我们来演示一下

 这样不就是了吗,这里异常报错是因为我们没有补完,因为我们最后返回的对象是个空嘛,然后上面最开始的又打印了最后的字符串,所以报错了

那我们现在完整的补

发现我们打印的结果是这个鬼样子,这个是为啥呢,这个不就是打印java中类的地址吗,只有调用的,这个不就是没有调用toString嘛,还记得我们当是不是通过toString来定位加密位置的,所以我们抠代码要把toString带上,那个相当于重写

这样就出结果啦

这里给出完整补环境代码

//放到sign内部
    @Override
    public boolean callBooleanMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        if(signature.equals("java/util/Map->isEmpty()Z")){
            //这个就是判断是不是这个字典是不是空嘛
            //方式一,直接返回True,因为我们已经知道这个字典不是空
            //return true;

            //方式二 主动调用,取出传入的对象
            Map m=(Map)dvmObject.getValue();
            // 打印这个map看一下,就是我们拼凑出来,传入的
            return m.isEmpty();
        }

        return super.callBooleanMethod(vm, dvmObject, signature, varArg);
    }

    @Override
    public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        if(signature.equals("java/util/Map->get(Ljava/lang/Object;)Ljava/lang/Object;")){
            // 报错是:map根据key去取值--》c调用java--》map.get('actual_played_time')
            // 方式一:使用Object泛指:
            // Map m =(Map) dvmObject.getValue();
            // //取出参数:取第0个参数--》我们这个只有一个参数
            //Object key=varArg.getObjectArg(0).getValue();
            // Object value=m.get(key);
            // return ProxyDvmObject.createObject(vm,value);

            //方式二,知道了具体是string类型
            Map m =(Map) dvmObject.getValue();
            //取出参数:取第0个参数--》我们这个只有一个参数
            String key=(String)varArg.getObjectArg(0).getValue();
            String value=(String)m.get(key);
            return new StringObject(vm,value);
        }


        return super.callObjectMethod(vm, dvmObject, signature, varArg);
    }

    @Override
    public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        if(signature.equals("com/bilibili/nativelibrary/SignedQuery->r(Ljava/util/Map;)Ljava/lang/String;")){
            // SignedQuery对象--》调用r方法,传入map,返回字符串
            // 首先拿到参数 varArg.getObjectArg
            Map m =(Map) varArg.getObjectArg(0).getValue();

            // 然后我们取对象就不能用dvmObject,因为这个里面没有SignedQuery,apk定义的,得我们主动创建
            String res=SignedQuery.r(m);
            return new StringObject(vm,res);
        }


        return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
    }

    @Override
    public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        if(signature.equals("com/bilibili/nativelibrary/SignedQuery-><init>(Ljava/lang/String;Ljava/lang/String;)V")){
            // 方式一:打印sign 第二个参数就是
            //  String sign=(String) varArg.getObjectArg(1).getValue();
            // System.out.println(sign);
            // return vm.resolveClass("com/bilibili/nativelibrary/SignedQuery").newObject(null);

            // 方式二:补齐,实例化得到对象
            String v1=(String) varArg.getObjectArg(0).getValue();
            String sign=(String) varArg.getObjectArg(1).getValue();
            SignedQuery sq=new SignedQuery(v1,sign);
            return vm.resolveClass("com/bilibili/nativelibrary/SignedQuery").newObject(sq);


        }

        return super.newObject(vm, dvmClass, signature, varArg);
    }


//定义类,单独放
class SignedQuery{
    public final String a;
    public final String b;
    private static final char[] f15567c = "0123456789ABCDEF".toCharArray();


    public SignedQuery(String str, String str2) {
        this.a = str;
        this.b = str2;
    }




    static String r(Map<String, String> map) {
        if (!(map instanceof SortedMap)) {
            map = new TreeMap(map);
        }
        StringBuilder sb = new StringBuilder(256);
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String key = entry.getKey();
            if (!key.isEmpty()) {
                sb.append(b(key));
                sb.append("=");
                String value = entry.getValue();
                sb.append(value == null ? "" : b(value));
                sb.append("&");
            }
        }
        int length = sb.length();
        if (length > 0) {
            sb.deleteCharAt(length - 1);
        }
        if (length == 0) {
            return null;
        }
        return sb.toString();
    }

    static String b(String str) {
        return c(str, null);
    }

    static String c(String str, String str2) {
        StringBuilder sb = null;
        if (str == null) {
            return null;
        }
        int length = str.length();
        int i = 0;
        while (i < length) {
            int i2 = i;
            while (i2 < length && a(str.charAt(i2), str2)) {
                i2++;
            }
            if (i2 == length) {
                if (i == 0) {
                    return str;
                }
                sb.append((CharSequence) str, i, length);
                return sb.toString();
            }
            if (sb == null) {
                sb = new StringBuilder();
            }
            if (i2 > i) {
                sb.append((CharSequence) str, i, i2);
            }
            i = i2 + 1;
            while (i < length && !a(str.charAt(i), str2)) {
                i++;
            }
            try {
                byte[] bytes = str.substring(i2, i).getBytes("UTF-8");
                int length2 = bytes.length;
                for (int i3 = 0; i3 < length2; i3++) {
                    sb.append('%');
                    sb.append(f15567c[(bytes[i3] & 240) >> 4]);
                    sb.append(f15567c[bytes[i3] & 15]);
                }
            } catch (UnsupportedEncodingException e) {
                throw new AssertionError(e);
            }
        }
        return sb == null ? str : sb.toString();
    }

    private static boolean a(char c3, String str) {
        return (c3 >= 'A' && c3 <= 'Z') || (c3 >= 'a' && c3 <= 'z') || !((c3 < '0' || c3 > '9') && "-_.~".indexOf(c3) == -1 && (str == null || str.indexOf(c3) == -1));
    }

    public String toString() {
        String str = this.a;
        if (str == null) {
            return "";
        }
        if (this.b == null) {
            return str;
        }
        return this.a + "&sign=" + this.b;
    }

}

三.PDD anti-token破解

3.1 目标

1.目标
    破解搜索功能请求头中的  anti-token
    
2.版本选择:v6.32.0
1 通过hook查找方法在那个so文件中--》之前学过
    -动态注册脚本,静态注册脚本
    
3.补环境:app内部的参数值,系统参数值---》通过frida-hook得到--》搜索得到写死

4.使用SocksDroid转发再进行抓包

3.2 抓包和反编译搜索

 我们发现有时候配置了 SocksDroid也抓不到,那我们在无网络的时候进入,然后有网络了立马抓,这样就能抓到了

我们要破的就是这个吗,那我们现在来发编译搜索

我们直接搜索anti-token

 

发现很多地方,我这里直接告诉大家是第四个,我们点进去

发现f就是那个,那我们点进去看看

 

发现f是是一个接口,那我们是不是要找到这个接口的具体实现类,那我们查找这个接口的用例

这里我告诉大家是最后一个,我们点进去

我们找到这个f的实现类在这里,我们再点进去

 

我们再点进去

 

发现在这里,这不就是jni的方法吗,但是这里没有给出哪个so文件,我们之前给大家说过hook静态注册和动态注册,不知道大家记不记得,但是在hook之前我们先hook确定传入的参数,第一个参数是context,我们先不管,看看另一个参数是啥

 发现参数就是时间戳

那我们接下来就hook一下是静态注册和动态注册来判断so文件了,我直接告诉大家是动态注册,大家也可以去试试

hook完长这样 说明是在libpdd_secure.so里面,那我们把这两个都c到unidbg里面去

3.3 unidbg跑

我们编写好主要的初始化函数和sign方法

 

我们运行一下

 发现报错,那我们开始补环境

3.4 补环境

3.4.1 补腾讯日志库

我们在刚才报错截图里可以看到tencent,那我们百度搜索一下,这个是腾讯日志库,那我们就补

 他这个是日志,而且是void,我们直接return; 就好了

3.4.2 补手机权限

我们搜索发现 这个是获取手机权限的

1 报错是: android/content/Context->checkSelfPermission(Ljava/lang/String;)I
2 安卓开发者--》在Manifest文件中添加权限
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
3 用户使用app,会弹窗提醒

4  ContextCompat.checkSelfPermission()方法检测授权状态,返回的结果为PackageManager中的两个常量:PERMISSION_GRANTED(已授权)和PERMISSION_DENIED(未授权)
    public static final int PERMISSION_DENIED = -1; # 未授权
    public static final int PERMISSION_GRANTED = 0; # 授权
    
    
5 获取这个返回值的方式:
    获取方式一:我们可以通过hook拿到
    获取方式二:我们直接搜索写死-1或0
    获取方式三:我们自己写安卓应该,看自己手机的状态

那我们直接返回0,其实要返回-1的,后面出问题了再解释

 

3.4.3 getSystemService

再次运行

1  android/content/Context->getSystemService(Ljava/lang/String;)Ljava/lang/Object;
    -获得SystemService,后期为了拿到:sim卡状态,网络状态,运行商,电池。。。
    
2 安卓中对应
/*
* SIM的状态信息:
*  SIM_STATE_UNKNOWN             未知状态 0
*  SIM_STATE_ABSENT                 没插卡 1
*  SIM_STATE_PIN_REQUIRED     锁定状态,需要用户的PIN码解锁 2
*  SIM_STATE_PUK_REQUIRED    锁定状态,需要用户的PUK码解锁 3
*  SIM_STATE_NETWORK_LOCKED   锁定状态,需要网络的PIN码解锁 4
*  SIM_STATE_READY            就绪状态 5
*/ 

 我们再运行

发现这个错和补环境没关系,那是因为安卓11之后不允许获取手机权限, 所以得返回-1

改完之后再运行

 

 这里我就不和前面一样说为什么了,大家百度搜一下就知道啦,然后补系统方法,我就一次补好,我和大家说一下补app自己的方法

3.4.4 补app自己的方法

我们发现报这个错误,一是可以去app中找逻辑,二是可以直接hook,发现是个固定值,这里就不给出hook脚本了

 

3.4.5 补系统方法isDebuggerConnected

isDebuggerConnected是否有调试器挂载到程序上

运行

 

3.4.6 补异常

后面的报错我就先过,自己搜搜就知道了

3.4.7 补replace

补到发现报这个错,这个就是replace,要动脑子,我教大家补

 

3.4.8 补文件操作

发现报这个错,这个是文件的,我一次给大家补好

 

 

这些都补好之后,正常出值了

 

3.4.9 补环境代码

代码如下


    @Override
    public void callStaticVoidMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
        if(signature.equals("com/tencent/mars/xlog/PLog->i(Ljava/lang/String;Ljava/lang/String;)V")){
            return;
        }

        super.callStaticVoidMethodV(vm, dvmClass, signature, vaList);
    }

    @Override
    public int callIntMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        if(signature.equals("android/content/Context->checkSelfPermission(Ljava/lang/String;)I")){
            String s=(String) varArg.getObjectArg(0).getValue();
            // System.out.println(s); // android.permission.READ_PHONE_STATE
            return -1;
        }
        if(signature.equals("android/telephony/TelephonyManager->getSimState()I")){
            return 5; // 表示没插卡
        }

        if (signature.equals("android/telephony/TelephonyManager->getNetworkType()I")) {
            // 网络类型:https://codeleading.com/article/33471321733/
            return 13;
        }
        if (signature.equals("android/telephony/TelephonyManager->getDataState()I")) {
            return 0;
        }
        if (signature.equals("android/telephony/TelephonyManager->getDataActivity()I")) {
            return 4;
        }
        return super.callIntMethod(vm, dvmObject, signature, varArg);
    }

// 补:callObjectMethod

    @Override
    public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        if(signature.equals("android/content/Context->getSystemService(Ljava/lang/String;)Ljava/lang/Object;")){
            // 补个null---》后续,在c中调用 SystemService 获取手机状态信息--》再缺什么就继续补:sim卡信息,运行上信息。。。
            return vm.resolveClass("android/telephony/TelephonyManager").newObject(null);
            //不能用 ProxyDvmObject.createObject(vm,null) 补

        }
        if(signature.equals("android/telephony/TelephonyManager->getSimOperatorName()Ljava/lang/String;")){
            // 拿运营商:getSimOperatorName
            return new StringObject(vm, "中国电信");
        }
        if(signature.equals("android/telephony/TelephonyManager->getSimCountryIso()Ljava/lang/String;")){
            // 拿地区
            return new StringObject(vm, "cn");
        }
        if (signature.equals("android/telephony/TelephonyManager->getNetworkOperator()Ljava/lang/String;")) {
            //https://blog.csdn.net/ztp800201/article/details/44198031/
            return new StringObject(vm, "46003");
        }
        if (signature.equals("android/telephony/TelephonyManager->getNetworkOperatorName()Ljava/lang/String;")) {
            //https://blog.csdn.net/Myfittinglife/article/details/118685804
            return new StringObject(vm, "中国电信");
        }
        if (signature.equals("android/telephony/TelephonyManager->getNetworkCountryIso()Ljava/lang/String;")) {
            // 获取国家代码
            return new StringObject(vm, "cn");
        }
        if (signature.equals("java/lang/Throwable->getStackTrace()[Ljava/lang/StackTraceElement;")) {
            return new ArrayObject(vm.resolveClass("java/lang/StackTraceElement").newObject(null));
        }
        if (signature.equals("java/lang/StackTraceElement->getClassName()Ljava/lang/String;")) {
            return new StringObject(vm, "");
        }

        if (signature.equals("java/io/ByteArrayOutputStream->toByteArray()[B")) {
            ByteArrayOutputStream obj = (ByteArrayOutputStream) dvmObject.getValue();
            return new ByteArray(vm, obj.toByteArray());
        }
        return super.callObjectMethod(vm, dvmObject, signature, varArg);
    }

    @Override
    public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
        if (signature.equals("com/xunmeng/pinduoduo/secure/EU->gad()Ljava/lang/String;")) {
            return new StringObject(vm, "7202111111112f2");
        }
        return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
    }

    @Override
    public boolean callStaticBooleanMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        if (signature.equals("android/os/Debug->isDebuggerConnected()Z")) {
            return false;
        }
        return super.callStaticBooleanMethod(vm, dvmClass, signature, varArg);
    }

    @Override
    public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        if (signature.equals("java/lang/Throwable-><init>()V")) {
            Throwable t=new Throwable(); // 可以new出来,可以传null
            return vm.resolveClass("java/lang/Throwable").newObject(t);
        }
        //补文件
        if (signature.equals("java/io/ByteArrayOutputStream-><init>()V")) {
            ByteArrayOutputStream obj = new ByteArrayOutputStream();
            return vm.resolveClass("java/io/ByteArrayOutputStream").newObject(obj);
        }
        if (signature.equals("java/util/zip/GZIPOutputStream-><init>(Ljava/io/OutputStream;)V")) {
            try {
                OutputStream chunk = (OutputStream) varArg.getObjectArg(0).getValue();
                GZIPOutputStream obj = new GZIPOutputStream(chunk);
                return vm.resolveClass("java/util/zip/GZIPOutputStream").newObject(obj);
            } catch (Exception e) {
                System.out.println("写入错误1" + e);
            }
        }


        return super.newObject(vm, dvmClass, signature, varArg);
    }
    public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
        if (signature.equals("java/lang/String->replaceAll(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;")) {
            String origin = (String) dvmObject.getValue();
            String a0 = (String) vaList.getObjectArg(0).getValue();
            String a1 = (String) vaList.getObjectArg(1).getValue();
            String result = origin.replaceAll(a0, a1);
            return new StringObject(vm, result);
        }
        return super.callObjectMethodV(vm, dvmObject, signature, vaList);
    }

    public void callVoidMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        if (signature.equals("java/util/zip/GZIPOutputStream->write([B)V")) {
            GZIPOutputStream obj = (GZIPOutputStream) dvmObject.getValue();
            byte[] chunk = (byte[]) varArg.getObjectArg(0).getValue();
            try {
                obj.write(chunk);
            } catch (Exception e) {
            }
            return;
        }
        if (signature.equals("java/util/zip/GZIPOutputStream->finish()V")) {
            GZIPOutputStream obj = (GZIPOutputStream) dvmObject.getValue();
            try {
                obj.finish();
            } catch (Exception e) {
            }
            return;
        }
        if (signature.equals("java/util/zip/GZIPOutputStream->close()V")) {
            GZIPOutputStream obj = (GZIPOutputStream) dvmObject.getValue();
            try {
                obj.close();
            } catch (Exception e) {
            }
            return;
        }
        super.callVoidMethod(vm, dvmObject, signature, varArg);
    }

四.总结 

今天的内容很多,主要pdd的环境是真的难,难所以值钱呀,我至少写了10个小时,感谢点赞关注加收藏,发现补环境是不是很恼火,没办法,纯算更难,加油补吧!

补充

有需要源码的看我主页签名名字私

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1975836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何建立与众不同的市场洞察能力【深度】

来源&#xff1a;战略研发领航 建立与众不同的市场洞察机制&#xff0c;展示了如何在组织中建立一种以数据和洞察为核心的文化&#xff0c;并通过4S周期&#xff08;架构、探查、塑造、成型&#xff09;的洞察工作方法论&#xff0c;指导领导者和团队在各个层级上进行更有效的思…

【C语言】C语言期末突击/考研--函数

目录 一、函数的声明与定义-嵌套调用 1.1.函数的声明与定义 1.2.函数的分类与调用 二、函数的递归调用 三、局部变量与全局变量 3.1.全局变量解析形参实参解析 3.2.局部变量与全局变量 四、练习题及解析 一、函数的声明与定义-嵌套调用 1.1.函数的声明与定义 函数间的…

Code Review 这件事,AI 比人类强 10 倍

1.引言 代码审查&#xff0c;也就是 Code Review&#xff08;以下简称 CR&#xff09;&#xff0c;一直是 IT 行业讨论的热门话题&#xff0c;因为它在很大程度上决定了公司的工程质量。 但是&#xff0c;人工 CR 的成本的是很高的&#xff0c;所以在实际落地场景&#xff0c…

latex换行\left[和\right]编译报错-解决方案

简而言之&#xff1a;\\ 换成 \right.\\ , & 换成 &\left. 来个例子就知道了&#xff1a; 原本的公式是&#xff1a; \begin{align}\label{up_critic} L_Q(\theta) & \mathbb{E}\left[\frac{1}{2}(Q_\theta(\mathcal{S}_{k,t}^m, {A}_{k,t}^m) - ({R}_{k,t}^m …

07030405复杂可编程逻辑器件CPLD现场可编程阵列FPGA

复杂可编程逻辑器件CPLD&现场可编程阵列FPGA 7.3 复杂可编程逻辑器件CPLD7.3.1CPLD的结构 7.4现场可编程门阵列FPGA7.4.1FPGA实现逻辑功能的基本原理7.4.2FPGA结构简介1.可编程逻辑块2.I/O块3.可编程连线资源CPLD与FPGA的区别 7.5可编程逻辑器件开发过程简介编程条件 7.3 复…

细说文件操作

你好&#xff01;感谢支持孔乙己的新作&#xff0c;本文就结构体与大家分析我的思路。 希望能大佬们多多纠正及支持 &#xff01;&#xff01;&#xff01; 个人主页&#xff1a;爱摸鱼的孔乙己-CSDN博客 目录 1.什么是文件 1.1.程序设计文件 1.1.1.程序文件 1.1.2.数据文件…

使用EntityFramework8的学习和开发过程中一些经验

前言&#xff1a; 本篇文章主要记录自己在EF8的学习和使用过程中的碰到一些坑和自己琢磨的一些解决问题的方法以及过程。 问题汇总&#xff1a; 一、Code First 模式下生成表中字段的没有按照我们想要的次序排列 实体上增加注解 [Column(Order 0)] 可以通过Order来控制字段…

[CISCN2019 华北赛区 Day1 Web1]Dropbox 1

目录 题目分析功能测试代码读取index.phpdownload.phpdelete.phpclass.php 关键代码审计user类FileList类File类思路 解题步骤php脚本解题 题目分析 功能测试 注册登录后来到上传文件界面&#xff0c;通过改后缀&#xff0c;改文件头&#xff0c;改content-type&#xff0c;上…

如何养护我们的头发一

正常头发含水量10-13% 头发含水量低 头发就会干燥 受到损伤 头发保水能力弱 保养头发,最重要的是防止头发头发的流水. 正确的洗涤方法 干性头发,一般3天洗一次 油性头发,一般1天洗一次 中性头发,一般2天洗一次 (2)正确的洗发步骤 选择好梳子建议选用木梳或牛角梳 按摩头皮…

释疑 803-(2)物理层 整理总结

目录 2-01 物理层要解决哪些问题?物理层的主要特点是什么? 2-02 规程与协议有什么区别? 2-03 试给出数据通信系统的模型并说明其主要组成构件的作用。 2-04 试解释以下名词&#xff1a;数据、信号、模拟数据、模拟信号、基带信号、带通信号、数字数据、数字信号、码元、…

PHP智能问诊导诊平台-计算机毕业设计源码75056

摘 要 智能问诊导诊平台作为一种智能化医疗服务工具&#xff0c;利用PHP语言开发&#xff0c;旨在为用户提供便捷的在线问诊和导诊服务。该平台集成了智能算法和医疗数据&#xff0c;实现了智能化的病情诊断和治疗建议&#xff0c;帮助用户更快速地获取医疗信息和建议。用户可…

未授权访问

一、Redis 未授权访问 redis-cli -h 192.168.4.176-p6379 &#xff08;使用这个连接&#xff09; 连接成功后使用info测试 二、MongDB 未授权访问 使用fofa搜mongdb端口复制ip到Navicat,连接 】 三、Zookeeper未授权访问漏洞 使用fofa 搜索2181端口使用下面命令在kali在…

5、SystemC行为级建模

1、sc_port关联sc_interface和channel。sc_module聚合sc_module、var、process和sc_port。sc_cthread和sc_method继承sc_thread。 sc_interface中声明很多虚函数&#xff0c;作为接口函数实现接口功能&#xff0c;如send()、recv()等。可以通过发送和接收模块的端口调用(sc_in…

CCF编程能力等级认证GESP—C++3级—20240629

CCF编程能力等级认证GESP—C3级—20240629 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09;判断题&#xff08;每题 2 分&#xff0c;共 20 分&#xff09;编程题 (每题 25 分&#xff0c;共 50 分)移位寻找倍数 单选题&#xff08;每题 2 分&#xff0c;共 30 分…

【基础篇】MySQL数据库详解:基础知识详解

一、SQL分类 1.DDL2.DML3.DQL4.DCL二、函数 1.字符串函数2.数值函数3.日期函数4.流程函数三、约束 1.概述2.约束演示3.外键约束四、多表查询 1.多表关系2.多表查询表述3.内连接4.外连接5.自连接6.子查询五、事务 1.事务简介2.事务操作3.事务四大特性4.并发事务问题5.事务隔离级…

C语言——求阶乘的两种方法

第一种方法使用了递归思想 #include <stdio.h> int fun(int N) {if (N 0){return 1;}else{return (fun(N - 1) * N);} } int main() {int N 0;scanf_s("%d", &N);printf("%d",fun(N)); } 第二种方法用的for循环 #include <stdio.h> i…

如何开启idea中的断言功能?

目录 一、什么是断言&#xff1f; 二、Java断言的语法 三、开启断言 一、什么是断言&#xff1f; 断言&#xff08;assert&#xff09;是 Java 中的一条语句&#xff0c;一种在程序中的逻辑&#xff08;如一个结果为真或假的逻辑判断式&#xff09;&#xff0c;目的是验证软…

Unity Shader unity文档学习笔记(十八):unity雾效原理

看很多文章用近平面远平面组成矩阵后转到裁剪空间下通过Z值来解&#xff0c;实际更简单的方式可以直接通过判断距离来实现 FogMgr控制远近面 public class TestFog : MonoBehaviour {public int startDis 0;public int endDis 50;public Vector4 fogParam;public void Awak…

springboot宠物宿舍管理系统-计算机毕业设计源码74795

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1系统开发流程 2.2.2 用户登录流程 2.2.3 系统操作流程 2.2.4 添加信息流程 2.2.5 修改信息流程 2.2.6 删除信息流程 2.3 系统功能…

javascript:判断输入值是数字还是字母

1 代码示例 要判断输入值是数字还是字母&#xff0c;我们可以通过JavaScript获取输入框的值&#xff0c;然后使用isNaN函数来检查输入值是否为数字。 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><s…